Share image from mpldatacursor with others

前端 未结 1 1442
南笙
南笙 2021-01-15 11:38

I am using the example at How to show data labels when you mouse over data to make an image where data appears when you mouse over points. This works really well but is ther

相关标签:
1条回答
  • 2021-01-15 12:13

    There may be ways to make things work with svg and a bit of javascript or pdf tooltips, as you suggested (I didn't know pdf tooltips existed until you mentioned them!).

    As an aside, I should take a moment to mention mpld3 which recreates matplotlib figures as javascript visualizations using d3. It does allow for very sharable, interactive figures and has some examples of making interactive tooltips.

    However, I'm not sure how to make the matplotlib's svg files generally interactive, and you mentioned that you'd rather not go the javascript route, so I'll walk you through building a "stand-alone" executable (or, rather, a directory with an executable and associated libraries).

    Building a "stand-alone" executable with cx_freeze and matplotlib

    At least with regards to packaging an executable, I'd recommend cx_freeze. There are plenty of other options (e.g. pyinstaller, py2exe, py2app, etc), but most of them are platform-specific and a bit too "magical" for my tastes. cx_freeze requires a bit more knowledge to use, but it's quite reliable, and not too hard to use once you're aware of what needs to be included.

    First off, the complete example I'm about to walk you through is available here: https://gist.github.com/joferkington/9214844 It uses an example script and data you gave as part of an earlier question.

    The key is to build a setup.py file that correctly references 1) matplotlib's data files and 2) any data you need to include with your code for it to run correctly.

    After that, it's as simple as python setup.py build_exe and tar the build directory it creates to send to other people. (You may likely want to do something a bit fancier. It's possible to make shell scripts that contain the tarred data, libraries, and executable, but I'll skip that part here.)

    Making the setup.py file

    On with the setup.py. Let's assume you have a simple script called plot.py that contains some basic plotting code and a file called data.csv with the data that you want to plot with matplotlib, etc. The setup.py file for cx_freeze would look something like this: (Also, for simplicity, I'm assuming you're using the Tk backend for matplotlib. Things will look slightly different if you're not.)

    import cx_Freeze
    import sys
    import matplotlib
    base = None
    if sys.platform == "win32":
        base = "Win32GUI"
    
    executables = [
        cx_Freeze.Executable("plot.py", base = base),
        ]
    
    build_exe_options = {"includes":["matplotlib.backends.backend_tkagg"],
                         "include_files":[(matplotlib.get_data_path(), "mpl-data"),
                                          ('data.csv', 'data.csv')],
                         "excludes":[],
                         }
    cx_Freeze.setup(
        name = "script",
        options = {"build_exe": build_exe_options},
        version = "0.0",
        description = "A basic example",
        executables = executables)
    

    Most of this is boilerplate. The key parts are:

    1. The name of your script (x_Freeze.Executable("plot.py", base = base))
    2. The "includes" section in the build_exe_options. cx_freeze will try to auto-guess what modules it needs to include, but there are cases where it's not possible to detect everything it needs. This section allows you to specify additional modules to explicitly include. The matplotlib backends usually aren't auto-detected correctly, so you'll need to explicitly include whichever backend you're using.
    3. The "include_files" section in the build_exe_options. This indicates any additional data files that need to be included. Matplotlib has some data files (icons, etc) that need to be shipped alongside the code and libraries for things to function correctly. The line (matplotlib.get_data_path(), "mpl-data") gets these files and puts them in a folder called "mpl-data" inside the build directory. Similarly, the line ('data.csv', 'data.csv') gets your "data.csv" file and saves it with the same name in the build directory.

    I'll take a second to mention the "excludes" option. This is entirely optional, but cx_freeze will usually include many libraries that aren't actually required for your script to function. If you want to slim down the size of the file you're distributing, you may want to list particular python modules to exclude here. (e.g. "excludes":['PyQt4', 'scipy'])

    The rest is fairly self-explanatory. You may want to fill in the description, version, etc, but it's not required to build an executable.

    Building

    So at this point, we have a directory with contents similar to the following:

    $ ls
    data.csv  plot.py  setup.py
    

    data.csv has our data, plot.py is the script to plot it, and setup.py is as described above.

    To build an executable, we'd run

    python setup.py build_exe
    

    You'll get a long log of the build and exactly what it's copying over (probably along with some warnings that can be safely ignored in most cases). (This is useful information for debugging what's wrong with your setup.py file.)

    After it completes you'll notice a new directory called build.

    $ ls
    build  data.csv  plot.py  setup.py
    

    At this point, build will contain a single directory named something similar to:

    $ ls build
    exe.linux-x86_64-2.7
    

    The exe.whatever directory contains the libraries, data, and executable that you'll need to distribute to people for things to run correctly.

    To see if it works, try (note the explict cd into the directory!! More on that in a bit.):

    $ cd build/exe.linux-x86_64-2.7
    $ ./plot
    

    (Obviously, if your file above wasn't called plot.py, the executable won't be called plot, but you get the idea.)

    At this point, you could tar up the exe.whatever directory (probably want to rename it before tarring), ship it out, and tell people to run it by untarring and calling cd name_of_dir; ./plot.

    Caveats about paths to data

    I mentioned that we currently need to explictly cd into the directory before running things. This is purely a result of the fact that plot.py looks for a file called data.csv in the current directory.

    In other words, there's a line in plot.py that does:

    df = pd.read_csv('data.csv', ...)
    

    We made setup.py smart enough to include data.csv but the code that reads it in expects it to be in the current directory.

    You have two options:

    1. Always cd into the directory before running the script (In practice, ship a short script that cds in, runs the program, and cds back out). This is useful as a last resort if you don't want to bother with the second option.
    2. Change your code to reference the data file relative to the script's location.

    The second option is better for a number of reasons, but you'll have to modify your script (plot.py, in this case) slightly.

    Normally, you'd use the path to __file__ to determine the location relative to the script itself. However, with cx_freeze, __file__ won't be defined, and the path you want is that of sys.executable instead. For that reason, you usually do something like this: (From the cx_freeze faq: http://cx-freeze.readthedocs.org/en/latest/faq.html#data-files)

    def find_data_file(filename):
        if getattr(sys, 'frozen', False):
            # The application is frozen
            datadir = os.path.dirname(sys.executable)
        else:
            # The application is not frozen
            # Change this bit to match where you store your data files:
            datadir = os.path.dirname(__file__)
    
        return os.path.join(datadir, filename)
    

    In that case, you'd modify your code that does:

    pd.read_csv('data.csv', ...)
    

    to do:

    pd.read_csv(find_data_file('data.csv'), ...) 
    

    instead. (This hasn't been done in the plot.py file in the gist I linked to originally. I'll leave it to the reader as an exercise.)

    Once we've done that, you can call /path/to/where/the/directory/gets/copied/plot directly regardless of what the current working directory is.

    Distributing

    I won't say too much on this topic. There are a lot of ways to handle this. With cx_freeze, you're shipping a folder full of libraries and a single executable.

    In the simplest case, you just tar it up, and tell people to untar and run where/they/extracted/it/name_of_the_execuctable. You might want to rename the folder from exe.linux-x86_64-2.7 to something more like my_package and include a shell script called run_this or something, but that's up to you.

    In other cases you may want to write a wrapper script or even a .desktop file. Desktop files have to have absolute paths, so you'll need to do a bit more in that case. Usually, you write an installer script of some sort that modifies whatever.desktop to point to the absolute path of where your program gets installed.

    It's possible to embed the tarred data, libraries, and executable into a "self-extracting" install script. There are examples on the web if you want to dig around for them. You could also build an .rpm or .deb. Again, I'll skip the detailed example and leave that to you to figure out.

    Overall, for what you seem to be doing, shipping a tarball and a README is probably the simplest route.

    0 讨论(0)
提交回复
热议问题