Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: install python
uses: actions/setup-python@v4
with:
python-version: "3.11"
python-version: "3.13"

- name: install package and dependencies
run: pip install -e . && pip install -r test_requirements.txt
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ All notable changes to this project will be documented in this file.

The format is based on `Keep a Changelog <https://keepachangelog.com>`_.

2.3.0
-----
Added
+++++
- Start testing on Python 3.13, and up minimum version to 3.9.
- Added ``marimo_utils.display_fig_marimo`` to enable easy display of large curves via `marimo`

Removed
+++++++
- Removed obsolete ``parse_excel`` module.

2.2.0
-----

Expand Down
2 changes: 1 addition & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Installation
--------------

`neutcurve` requires Python 3.8 or newer.
`neutcurve` requires Python 3.9 or newer.

The easiest way to install `neutcurve` is from `PyPI <https://pypi.org/>`_ using `pip <https://pip.pypa.io>`_ with::

Expand Down
2 changes: 1 addition & 1 deletion neutcurve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

__author__ = "Jesse Bloom"
__email__ = "jbloom@fredhutch.org"
__version__ = "2.2.0"
__version__ = "2.3.0"
__url__ = "https://github.com/jbloomlab/neutcurve"

from neutcurve.curvefits import CurveFits # noqa: F401
Expand Down
114 changes: 114 additions & 0 deletions neutcurve/marimo_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
============
marimo_utils
============

Utilities for working with `marimo <https://marimo.io/>`_ notebooks.
"""

import base64
import io

import matplotlib
import matplotlib.figure


def display_fig_marimo(fig, display_method):
"""Display large matplotlib figure via marimo.

This function is designed when you have a very large matplotlib figure (eg, as
produced by the plotting functions of :class:`neutcurve.curvefits.CurveFits`)
and you want to display it in marimo notebook.

Running this function requires you to have separately installed
`marimo <https://marimo.io/>`_ and (if you are using `display_method="png8")
`pillow <https://python-pillow.github.io/>`_

Args:
`fig` (matplotlib..figure.Figure)
The figure we want to display.
`display_method` {"inline", "svg", "pdf", "png8"}
Display the figure just inline, as a SVG, as a PDF, or as a PNG8.
In general, displaying as a PNG8 will be the smallest size although
also the lowest resolution.

Returns:
output_obj
The returned object can be display in marimo, via
``marimo.output.append(output_obj)``

"""
if not isinstance(fig, matplotlib.figure.Figure):
raise ValueError(
f"Expected `fig` to be matplotlib.figure.Figure, instead {type(fig)=}"
)

if display_method == "inline":
return fig

import marimo as mo

if display_method == "svg":
buf = io.BytesIO()
with matplotlib.rc_context(
{
"svg.fonttype": "none", # keep text as text, not paths
"svg.image_inline": True, # embed small images if present
"svg.hashsalt": "fixed-1", # deterministic ids in the SVG
"path.simplify": True,
"path.simplify_threshold": 0.2,
}
):
fig.savefig(buf, format="svg", metadata={})
svg_text = buf.getvalue().decode("utf-8")
return mo.Html(
f"""
<style>
#svgwrap svg {{
width: 100% !important;
height: auto !important;
max-width: 100%;
display: block;
}}
</style>
<div id="svgwrap" style="width:100%;height:80vh;overflow:auto">
{svg_text}
</div>
"""
)

elif display_method == "pdf":
buf = io.BytesIO()
with matplotlib.rc_context(
{
"pdf.fonttype": 42,
"pdf.compression": 7,
"path.simplify": True,
"path.simplify_threshold": 0.2,
}
):
fig.savefig(buf, format="pdf", metadata={})
buf.seek(0)
return mo.pdf(src=buf, width="100%", height="80vh")

elif display_method == "png8":
import PIL

buf = io.BytesIO()
fig.savefig(buf, format="png", dpi=80, metadata={})
im = PIL.Image.open(io.BytesIO(buf.getvalue())).quantize(
colors=48, dither=PIL.Image.NONE
)
out = io.BytesIO()
im.save(out, format="PNG", optimize=True, pnginfo=PIL.PngImagePlugin.PngInfo())
data = base64.b64encode(out.getvalue()).decode("ascii")
return mo.Html(f'<img src="data:image/png;base64,{data}" alt="figure">')

else:
raise ValueError(f"Invalid {display_method=}")


if __name__ == "__main__":
import doctest

doctest.testmod()
198 changes: 0 additions & 198 deletions neutcurve/parse_excel.py

This file was deleted.

4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
except ImportError:
raise ImportError("You must install `setuptools`")

if not (sys.version_info[0] == 3 and sys.version_info[1] >= 8):
if not (sys.version_info[0] == 3 and sys.version_info[1] >= 9):
raise RuntimeError(
"neutcurve requires Python 3.8 or higher.\n"
"neutcurve requires Python 3.9 or higher.\n"
f"You are using Python {sys.version_info[0]}.{sys.version_info[1]}"
)

Expand Down