tikzplot42 0.5.0__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tikzplot42-0.5.0/src/tikzplot42.egg-info → tikzplot42-0.5.1}/PKG-INFO +5 -3
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/README.md +4 -2
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/pyproject.toml +1 -1
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/axes.py +347 -21
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/axes.pyi +160 -8
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/elements.py +55 -2
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/elements.pyi +2 -1
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/plots.py +7 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/plots.pyi +128 -25
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/styles.py +11 -8
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/texts.py +1 -6
- {tikzplot42-0.5.0 → tikzplot42-0.5.1/src/tikzplot42.egg-info}/PKG-INFO +5 -3
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot42.egg-info/SOURCES.txt +1 -2
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test11.py +7 -3
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test14.py +3 -2
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test15.py +5 -2
- tikzplot42-0.5.0/tests/test_styles.py +0 -17
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/LICENSE +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/setup.cfg +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/__init__.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/__init__.pyi +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/axes3d.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/axes3d.pyi +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/colorbar.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/colorbar.pyi +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/colors.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/config.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/config.pyi +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/figure.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/figure.pyi +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/latex_special.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/py.typed +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/state.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot/styles.pyi +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot42.egg-info/dependency_links.txt +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot42.egg-info/requires.txt +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/src/tikzplot42.egg-info/top_level.txt +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test1.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test10.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test12.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test13.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test2.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test3.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test4.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test5.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test6.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test7.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test8.py +0 -0
- {tikzplot42-0.5.0 → tikzplot42-0.5.1}/tests/test9.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tikzplot42
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: TikzPlot to make TikZ/PGFPlots plots with matplotlib.pyplot-like syntax.
|
|
5
5
|
Author-email: Zan Ambrozic <zan.ambrozic7@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -740,6 +740,7 @@ Some basic plot commands are already implemented with commonly used arguments:
|
|
|
740
740
|
- `hlines()/vlines()`,
|
|
741
741
|
- `hist()`,
|
|
742
742
|
- `bar()/barh()`,
|
|
743
|
+
- `bar_label()`,
|
|
743
744
|
- `step()`,
|
|
744
745
|
- `imshow()`,
|
|
745
746
|
- `text()`,
|
|
@@ -763,7 +764,8 @@ If axes and plot have different name for command with same effect, both are impl
|
|
|
763
764
|
- `ax.set_xticklabels()/ax.set_yticklabels()`,
|
|
764
765
|
- `legend()` (basic position control),
|
|
765
766
|
- `ax.twinx()`,
|
|
766
|
-
- `ax.set()
|
|
767
|
+
- `ax.set()`,
|
|
768
|
+
- `ax.tick_params()`,
|
|
767
769
|
|
|
768
770
|
There is a limited support for built-in styles (e.g. `plt.style.use("default")`), you may also define your own style using `plt.style.set_profile()`. Currently supported features are: color cycle (hex colors sequence), line width (in pt), grid (with matplotlib kwargs), background (latex style key=value), additional settings (raw latex code for optional arguments to axis: key=value).
|
|
769
771
|
|
|
@@ -790,6 +792,6 @@ For plot configurations (default sizes, paddings, etc.), use `from tikzplot impo
|
|
|
790
792
|
Currently supported parameters are listed with their default values in `src/tikzplot/config.py`.
|
|
791
793
|
|
|
792
794
|
## More features coming soon...
|
|
793
|
-
- additional arguments
|
|
795
|
+
- additional arguments support for implemented commands,
|
|
794
796
|
- improved 3D plots (currently only basic support: plot, errorbar, fill_between, scatter, plot_wireframe, plot_surface),
|
|
795
797
|
- ...
|
|
@@ -41,6 +41,7 @@ Some basic plot commands are already implemented with commonly used arguments:
|
|
|
41
41
|
- `hlines()/vlines()`,
|
|
42
42
|
- `hist()`,
|
|
43
43
|
- `bar()/barh()`,
|
|
44
|
+
- `bar_label()`,
|
|
44
45
|
- `step()`,
|
|
45
46
|
- `imshow()`,
|
|
46
47
|
- `text()`,
|
|
@@ -64,7 +65,8 @@ If axes and plot have different name for command with same effect, both are impl
|
|
|
64
65
|
- `ax.set_xticklabels()/ax.set_yticklabels()`,
|
|
65
66
|
- `legend()` (basic position control),
|
|
66
67
|
- `ax.twinx()`,
|
|
67
|
-
- `ax.set()
|
|
68
|
+
- `ax.set()`,
|
|
69
|
+
- `ax.tick_params()`,
|
|
68
70
|
|
|
69
71
|
There is a limited support for built-in styles (e.g. `plt.style.use("default")`), you may also define your own style using `plt.style.set_profile()`. Currently supported features are: color cycle (hex colors sequence), line width (in pt), grid (with matplotlib kwargs), background (latex style key=value), additional settings (raw latex code for optional arguments to axis: key=value).
|
|
70
72
|
|
|
@@ -91,6 +93,6 @@ For plot configurations (default sizes, paddings, etc.), use `from tikzplot impo
|
|
|
91
93
|
Currently supported parameters are listed with their default values in `src/tikzplot/config.py`.
|
|
92
94
|
|
|
93
95
|
## More features coming soon...
|
|
94
|
-
- additional arguments
|
|
96
|
+
- additional arguments support for implemented commands,
|
|
95
97
|
- improved 3D plots (currently only basic support: plot, errorbar, fill_between, scatter, plot_wireframe, plot_surface),
|
|
96
98
|
- ...
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tikzplot42"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.1"
|
|
8
8
|
description = "TikzPlot to make TikZ/PGFPlots plots with matplotlib.pyplot-like syntax."
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Zan Ambrozic", email = "zan.ambrozic7@gmail.com" }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from multiprocessing import parent_process
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
2
|
+
from typing import Any, Iterable
|
|
3
|
+
import copy
|
|
4
4
|
|
|
5
5
|
import numpy as _np
|
|
6
6
|
import matplotlib.pyplot as _plt
|
|
@@ -41,6 +41,7 @@ class BaseAxes:
|
|
|
41
41
|
self._int_ymax = None
|
|
42
42
|
|
|
43
43
|
self._preferred_lims = {}
|
|
44
|
+
self._bar_labels = {}
|
|
44
45
|
|
|
45
46
|
def _get_overlay(self):
|
|
46
47
|
return sorted(self._elements.keys())[-1]
|
|
@@ -70,6 +71,80 @@ class BaseAxes:
|
|
|
70
71
|
def _check_approximate_equal(self, a, b, tol=1e-5):
|
|
71
72
|
return _np.count_nonzero(_np.abs(_np.asarray(a) - _np.asarray(b))/_np.max(_np.abs(b)) > tol) == 0
|
|
72
73
|
|
|
74
|
+
def _update_axis_options(self, key, value):
|
|
75
|
+
accepted = {"label style": (dict, " "), "tick style": (dict, " "), "tick label style": (dict, " "), "tick align": (str, "")}
|
|
76
|
+
if isinstance(value, dict):
|
|
77
|
+
value = value.copy()
|
|
78
|
+
if key.startswith(("x", "y")):
|
|
79
|
+
k = key.removeprefix("x").removeprefix("y").strip()
|
|
80
|
+
set_ax = ("x" if key.startswith("x") else "y") + accepted[k][1]
|
|
81
|
+
other_ax = ("y" if set_ax.strip() == "x" else "x") + accepted[k][1]
|
|
82
|
+
if k not in accepted:
|
|
83
|
+
self._axis_options[key] = value
|
|
84
|
+
elif k in self._axis_options:
|
|
85
|
+
if accepted[k][0] != type(value):
|
|
86
|
+
raise ValueError(f"Value for {key} must be of type {accepted[k][0].__name__}.")
|
|
87
|
+
if accepted[k][0] == dict:
|
|
88
|
+
common_props = self._axis_options[k].copy()
|
|
89
|
+
setting_axis = {}
|
|
90
|
+
other_axis = {}
|
|
91
|
+
for vk in list(value.keys()).copy():
|
|
92
|
+
if vk in common_props:
|
|
93
|
+
if common_props[vk] == value[vk]:
|
|
94
|
+
value.pop(vk)
|
|
95
|
+
else:
|
|
96
|
+
other_axis[vk] = common_props.pop(vk)
|
|
97
|
+
self._axis_options[k].pop(vk)
|
|
98
|
+
setting_axis[vk] = value[vk]
|
|
99
|
+
else:
|
|
100
|
+
setting_axis[vk] = value[vk]
|
|
101
|
+
if other_axis:
|
|
102
|
+
if f"{other_ax}{k}" not in self._axis_options:
|
|
103
|
+
self._axis_options[f"{other_ax}{k}"] = {}
|
|
104
|
+
self._axis_options[f"{other_ax}{k}"].update(other_axis)
|
|
105
|
+
if setting_axis:
|
|
106
|
+
if f"{set_ax}{k}" not in self._axis_options:
|
|
107
|
+
self._axis_options[f"{set_ax}{k}"] = {}
|
|
108
|
+
self._axis_options[f"{set_ax}{k}"].update(setting_axis)
|
|
109
|
+
if common_props:
|
|
110
|
+
self._axis_options[k].update(common_props)
|
|
111
|
+
elif accepted[k][0] == str:
|
|
112
|
+
if self._axis_options[k] != value:
|
|
113
|
+
self._axis_options[f"{set_ax}{k}"] = value
|
|
114
|
+
self._axis_options[f"{other_ax}{k}"] = self._axis_options.pop(k)
|
|
115
|
+
else:
|
|
116
|
+
if key not in self._axis_options and accepted[k][0] == dict:
|
|
117
|
+
self._axis_options[key] = {}
|
|
118
|
+
if accepted[k][0] == dict:
|
|
119
|
+
self._axis_options[key].update(value)
|
|
120
|
+
else:
|
|
121
|
+
self._axis_options[key] = value
|
|
122
|
+
else:
|
|
123
|
+
if key not in accepted:
|
|
124
|
+
self._axis_options[key] = value
|
|
125
|
+
elif accepted[key][0] != type(value):
|
|
126
|
+
raise ValueError(f"Value for {key} must be of type {accepted[key][0].__name__}.")
|
|
127
|
+
else:
|
|
128
|
+
if "x" + accepted[key][1] + key in self._axis_options:
|
|
129
|
+
if accepted[key][0] == dict:
|
|
130
|
+
for vk in list(value.keys()).copy():
|
|
131
|
+
if vk in self._axis_options["x" + accepted[key][1] + key]:
|
|
132
|
+
self._axis_options["x" + accepted[key][1] + key].pop(vk)
|
|
133
|
+
elif accepted[key][0] == str:
|
|
134
|
+
self._axis_options.pop("x" + accepted[key][1] + key)
|
|
135
|
+
if "y" + accepted[key][1] + key in self._axis_options:
|
|
136
|
+
if accepted[key][0] == dict:
|
|
137
|
+
for vk in list(value.keys()).copy():
|
|
138
|
+
if vk in self._axis_options["y" + accepted[key][1] + key]:
|
|
139
|
+
self._axis_options["y" + accepted[key][1] + key].pop(vk)
|
|
140
|
+
elif accepted[key][0] == str:
|
|
141
|
+
self._axis_options.pop("y" + accepted[key][1] + key)
|
|
142
|
+
if key not in self._axis_options and accepted[key][0] == dict:
|
|
143
|
+
self._axis_options[key] = {}
|
|
144
|
+
if accepted[key][0] == dict:
|
|
145
|
+
self._axis_options[key].update(value)
|
|
146
|
+
self._axis_options[key] = value
|
|
147
|
+
|
|
73
148
|
def _plot(self, x, y, settings={}, xerr=None, yerr=None, overlay=None, note=None, **style):
|
|
74
149
|
spec = None
|
|
75
150
|
if self._get_overlay() in self._overlay_special:
|
|
@@ -572,6 +647,44 @@ class BaseAxes:
|
|
|
572
647
|
settings[bar_type] = None
|
|
573
648
|
return self._plot(k, v, yerr=kwargs.get("yerr", None), xerr=kwargs.get("xerr", None), overlay=overlay, settings=settings, note=note, **element_args)
|
|
574
649
|
|
|
650
|
+
def bar_label(self, container, labels=None, **kwargs):
|
|
651
|
+
kws = {"color", "c", "fontsize", "rotation", "fmt", "padding"}
|
|
652
|
+
kwargs = self._check_kwargs("bar_label", kws, **kwargs)
|
|
653
|
+
if not isinstance(container, Graph):
|
|
654
|
+
raise Warning("Container must be a Graph object.")
|
|
655
|
+
if labels is not None and labels != []:
|
|
656
|
+
if len(labels) != len(container._x):
|
|
657
|
+
raise Warning("Length of labels does not match number of bars.")
|
|
658
|
+
|
|
659
|
+
if "color" in kwargs or "c" in kwargs:
|
|
660
|
+
c = kwargs.pop("color", kwargs.pop("c", None))
|
|
661
|
+
kwargs["color_parsed"] = self._match_color(c)
|
|
662
|
+
if "fontsize" in kwargs:
|
|
663
|
+
kwargs["fontsize_parsed"] = self._tex_fontsize(kwargs["fontsize"])
|
|
664
|
+
o = self._get_element_overlay(container)
|
|
665
|
+
if o not in self._bar_labels:
|
|
666
|
+
self._bar_labels[o] = {}
|
|
667
|
+
if labels != []:
|
|
668
|
+
if labels is None:
|
|
669
|
+
labels = container._y
|
|
670
|
+
fmt = kwargs.pop("fmt", "%g")
|
|
671
|
+
if not isinstance(fmt, str):
|
|
672
|
+
raise Warning("fmt must be a string.")
|
|
673
|
+
def format_label(q):
|
|
674
|
+
if "{" in fmt and "}" in fmt:
|
|
675
|
+
return fmt.format(q)
|
|
676
|
+
return fmt % q
|
|
677
|
+
labels = [format_label(q) if isinstance(q, (int, float)) else q for q in labels]
|
|
678
|
+
cleaned = ["".join(c for c in s if c.isdigit() or c in ".-") for s in labels]
|
|
679
|
+
parsed = _np.array(cleaned, dtype=float)
|
|
680
|
+
all_match = _np.allclose(container._y, parsed)
|
|
681
|
+
if all_match:
|
|
682
|
+
labels = None
|
|
683
|
+
if labels is not None and labels != []:
|
|
684
|
+
container._add_meta_column(labels)
|
|
685
|
+
self._bar_labels[self._get_element_overlay(container)][container] = kwargs | {"bar_labels": labels}
|
|
686
|
+
|
|
687
|
+
|
|
575
688
|
def step(self, x, y, *args, **kwargs):
|
|
576
689
|
kws = {"fmt", "alpha", "color", "c", "linestyle", "ls", "linewidth", "lw", "marker", "markersize", "ms", "label", "where"}
|
|
577
690
|
kwargs = self._check_kwargs("step", kws, **kwargs)
|
|
@@ -616,7 +729,46 @@ class BaseAxes:
|
|
|
616
729
|
self._axis_args.add("set layers")
|
|
617
730
|
return self._plot([xmin, xmax], [ymin, ymax], settings={"axhspan": None}, **kwargs)
|
|
618
731
|
|
|
619
|
-
def set_ylabel(self, label):
|
|
732
|
+
def set_ylabel(self, label, **kwargs):
|
|
733
|
+
kws = {"fontsize", "color", "c", "loc", "rotate"}
|
|
734
|
+
kwargs = self._check_kwargs("set_ylabel", kws, **kwargs)
|
|
735
|
+
st = {}
|
|
736
|
+
if "fontsize" in kwargs:
|
|
737
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
738
|
+
if "color" in kwargs or "c" in kwargs:
|
|
739
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
740
|
+
st["text"] = self._match_color(c)
|
|
741
|
+
if "loc" in kwargs:
|
|
742
|
+
loc = kwargs["loc"]
|
|
743
|
+
if loc not in ["top", "center", "bottom"]:
|
|
744
|
+
raise Warning(f"Invalid loc: {loc}. Must be one of 'top', 'center', or 'bottom'.")
|
|
745
|
+
if loc == "top":
|
|
746
|
+
st["at"] = "{(yticklabel cs:1)}"
|
|
747
|
+
if isinstance(self, Secondary):
|
|
748
|
+
st["anchor"] = "south west"
|
|
749
|
+
else:
|
|
750
|
+
st["anchor"] = "south east"
|
|
751
|
+
elif loc == "bottom":
|
|
752
|
+
st["at"] = "{(yticklabel cs:0)}"
|
|
753
|
+
if isinstance(self, Secondary):
|
|
754
|
+
st["anchor"] = "south west"
|
|
755
|
+
else:
|
|
756
|
+
st["anchor"] = "south east"
|
|
757
|
+
else:
|
|
758
|
+
st["at"] = {}
|
|
759
|
+
st["anchor"] = {}
|
|
760
|
+
if "rotate" in kwargs:
|
|
761
|
+
if kwargs["rotate"] not in ["vertical", "horizontal"]:
|
|
762
|
+
raise Warning(f"Invalid rotate: {kwargs['rotate']}. Must be one of 'vertical' or 'horizontal'.")
|
|
763
|
+
if kwargs["rotate"] == "horizontal":
|
|
764
|
+
if isinstance(self, Secondary):
|
|
765
|
+
st["rotate"] = "-90"
|
|
766
|
+
else:
|
|
767
|
+
st["rotate"] = "-90"
|
|
768
|
+
else:
|
|
769
|
+
st["rotate"] = {}
|
|
770
|
+
if st:
|
|
771
|
+
self._update_axis_options("y label style", st)
|
|
620
772
|
self._axis_options["ylabel"] = f"{{{tex_text(label)}}}"
|
|
621
773
|
|
|
622
774
|
def set_ylim(self, *args, **kwargs):
|
|
@@ -651,7 +803,17 @@ class BaseAxes:
|
|
|
651
803
|
if "base" in kwargs:
|
|
652
804
|
self._axis_options["log basis y"] = kwargs["base"]
|
|
653
805
|
|
|
654
|
-
def set_yticks(self, ticks, labels=None):
|
|
806
|
+
def set_yticks(self, ticks, labels=None, **kwargs):
|
|
807
|
+
kws = {"color", "c", "fontsize"}
|
|
808
|
+
kwargs = self._check_kwargs("set_yticks", kws, **kwargs)
|
|
809
|
+
st = {}
|
|
810
|
+
if "color" in kwargs or "c" in kwargs:
|
|
811
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
812
|
+
st["text"] = self._match_color(c)
|
|
813
|
+
if "fontsize" in kwargs:
|
|
814
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
815
|
+
if st:
|
|
816
|
+
self._update_axis_options("y tick style", st)
|
|
655
817
|
if ticks:
|
|
656
818
|
s_ticks = map(str, ticks)
|
|
657
819
|
self._axis_options["ytick"]=f"{{{','.join(s_ticks)}}}"
|
|
@@ -664,13 +826,78 @@ class BaseAxes:
|
|
|
664
826
|
self._axis_options["yticks"]=r"{}"
|
|
665
827
|
self._yticks = False
|
|
666
828
|
|
|
667
|
-
def set_yticklabels(self, labels):
|
|
829
|
+
def set_yticklabels(self, labels, **kwargs):
|
|
830
|
+
kws = {"color", "c", "fontsize"}
|
|
831
|
+
kwargs = self._check_kwargs("set_yticklabels", kws, **kwargs)
|
|
832
|
+
st = {}
|
|
833
|
+
if "color" in kwargs or "c" in kwargs:
|
|
834
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
835
|
+
st["text"] = self._match_color(c)
|
|
836
|
+
if "fontsize" in kwargs:
|
|
837
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
838
|
+
if st:
|
|
839
|
+
self._update_axis_options("y tick style", st)
|
|
668
840
|
if labels:
|
|
669
841
|
self._axis_options["yticklabels"]=f"{{{tex_text(','.join(labels))}}}"
|
|
670
842
|
else:
|
|
671
843
|
self._axis_options["yticklabels"]=r"{}"
|
|
672
844
|
self._yticks = False
|
|
673
845
|
|
|
846
|
+
def tick_params(self, axis="both", **kwargs):
|
|
847
|
+
kws = {"color", "c", "labelsize", "labelcolor", "colors", "direction", "top", "bottom", "left", "right"}
|
|
848
|
+
kwargs = self._check_kwargs("tick_params", kws, **kwargs)
|
|
849
|
+
if axis not in ["x", "y", "both"]:
|
|
850
|
+
raise Warning(f"Invalid axis: {axis}. Must be one of 'x', 'y', or 'both'.")
|
|
851
|
+
if isinstance(self, Secondary):
|
|
852
|
+
if axis != "y":
|
|
853
|
+
raise Warning("tick_params only supports axis='y' for secondary axes.")
|
|
854
|
+
X_POS_MAP = {"top": (True, False), "bottom": (False, True), "both": (True, True), "none": (False, False)}
|
|
855
|
+
Y_POS_MAP = {"left": (True, False), "right": (False, True), "both": (True, True), "none": (False, False)}
|
|
856
|
+
xt_b, xt_t = X_POS_MAP.get(kwargs.pop("xtick pos", "both"), (True, True))
|
|
857
|
+
yt_l, yt_r = Y_POS_MAP.get(kwargs.pop("ytick pos", "both"), (True, True))
|
|
858
|
+
prefix = "x" if axis == "x" else ("y" if axis == "y" else "")
|
|
859
|
+
if "bottom" in kwargs:
|
|
860
|
+
if axis == "y":
|
|
861
|
+
raise Warning("Cannot set 'bottom' for y-axis.")
|
|
862
|
+
xt_b = kwargs.pop("bottom")
|
|
863
|
+
if "top" in kwargs:
|
|
864
|
+
if axis == "y":
|
|
865
|
+
raise Warning("Cannot set 'top' for y-axis.")
|
|
866
|
+
xt_t = kwargs.pop("top")
|
|
867
|
+
if "left" in kwargs:
|
|
868
|
+
if axis == "x":
|
|
869
|
+
raise Warning("Cannot set 'left' for x-axis.")
|
|
870
|
+
yt_l = kwargs.pop("left")
|
|
871
|
+
if "right" in kwargs:
|
|
872
|
+
if axis == "x":
|
|
873
|
+
raise Warning("Cannot set 'right' for x-axis.")
|
|
874
|
+
yt_r = kwargs.pop("right")
|
|
875
|
+
X_INV = {v: k for k, v in X_POS_MAP.items()}
|
|
876
|
+
Y_INV = {v: k for k, v in Y_POS_MAP.items()}
|
|
877
|
+
self._axis_options["xtick pos"] = X_INV[(xt_t, xt_b)]
|
|
878
|
+
self._axis_options["ytick pos"] = Y_INV[(yt_l, yt_r)]
|
|
879
|
+
if self._axis_options["xtick pos"] == "both":
|
|
880
|
+
self._axis_options.pop("xtick pos")
|
|
881
|
+
if "colors" in kwargs:
|
|
882
|
+
c = self._match_color(kwargs.pop("colors"))
|
|
883
|
+
self._update_axis_options(prefix + " tick style", {"draw": c})
|
|
884
|
+
self._update_axis_options(prefix + " tick label style", {"text": c})
|
|
885
|
+
if "color" in kwargs or "c" in kwargs:
|
|
886
|
+
c = self._match_color(kwargs.get("color", kwargs.get("c")))
|
|
887
|
+
self._update_axis_options(prefix + " tick style", {"draw": c})
|
|
888
|
+
if "labelcolor" in kwargs:
|
|
889
|
+
c = self._match_color(kwargs["labelcolor"])
|
|
890
|
+
self._update_axis_options(prefix + " tick label style", {"text": c})
|
|
891
|
+
if "labelsize" in kwargs:
|
|
892
|
+
fs = kwargs["labelsize"]
|
|
893
|
+
self._update_axis_options(prefix + " tick label style", {"font": self._tex_fontsize(fs)})
|
|
894
|
+
if "direction" in kwargs:
|
|
895
|
+
direction = kwargs["direction"]
|
|
896
|
+
if direction not in ["in", "out", "inout"]:
|
|
897
|
+
raise Warning(f"Invalid direction: {direction}. Must be one of 'in', 'out', or 'inout'.")
|
|
898
|
+
TICK_DIR_MAP = {"in": "inside", "out": "outside", "inout": "center"}
|
|
899
|
+
self._update_axis_options(prefix + "tick align", TICK_DIR_MAP[direction])
|
|
900
|
+
|
|
674
901
|
_LEGEND_LOC_MAP = ["best", "upper right", "upper left", "lower_left", "lower right", "right", "center left", "center right", "lower center", "upper center", "center"]
|
|
675
902
|
_ANCHOR_MAP = {"top": "north", "bottom": "south", "upper": "north", "lower": "south", "left": "west", "right": "east", "center": "center"}
|
|
676
903
|
_FONT_SIZE_MAP = {"xx-small": "tiny", "x-small": "scriptsize", "small": "footnotesize", "medium": "small", "large": "normalsize", "x-large": "large", "xx-large": "Large"}
|
|
@@ -775,6 +1002,8 @@ class BaseAxes:
|
|
|
775
1002
|
def text(self, x, y, s, **kwargs):
|
|
776
1003
|
kws = {"alpha", "color", "c", "fontsize", "on_top", "size", "backgroundcolor", "horizontalalignment", "ha", "verticalalignment", "va", "rotation", "label"}
|
|
777
1004
|
kwargs = self._check_kwargs("text", kws, **kwargs)
|
|
1005
|
+
if "fontsize" in kwargs or "size" in kwargs:
|
|
1006
|
+
kwargs["fontsize"] = kwargs.pop("size", kwargs.pop("fontsize"))
|
|
778
1007
|
on_top = kwargs.pop("on_top", True)
|
|
779
1008
|
if on_top:
|
|
780
1009
|
assert self._fig is not None
|
|
@@ -959,7 +1188,7 @@ class Axes(BaseAxes):
|
|
|
959
1188
|
_bcgnd = self._style._get_background_cycle()
|
|
960
1189
|
if _bcgnd is not None:
|
|
961
1190
|
self._axis_options["axis background/.style"] = f"{{{_bcgnd}}}"
|
|
962
|
-
_add_settgs = self._style._get_additional_settings()
|
|
1191
|
+
_add_settgs = copy.deepcopy(self._style._get_additional_settings())
|
|
963
1192
|
if _add_settgs is not None:
|
|
964
1193
|
self._axis_options = _add_settgs | self._axis_options
|
|
965
1194
|
|
|
@@ -1002,10 +1231,63 @@ class Axes(BaseAxes):
|
|
|
1002
1231
|
cmap = "viridis"
|
|
1003
1232
|
return (self, cmap, m, M)
|
|
1004
1233
|
|
|
1005
|
-
def set_xlabel(self, label):
|
|
1234
|
+
def set_xlabel(self, label, **kwargs):
|
|
1235
|
+
kws = {"fontsize", "color", "c", "loc", "rotate"}
|
|
1236
|
+
kwargs = self._check_kwargs("set_xlabel", kws, **kwargs)
|
|
1237
|
+
st = {}
|
|
1238
|
+
if "fontsize" in kwargs:
|
|
1239
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
1240
|
+
if "color" in kwargs or "c" in kwargs:
|
|
1241
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
1242
|
+
st["text"] = self._match_color(c)
|
|
1243
|
+
if "loc" in kwargs:
|
|
1244
|
+
loc = kwargs["loc"]
|
|
1245
|
+
if loc not in ["left", "center", "right"]:
|
|
1246
|
+
raise Warning(f"Invalid loc: {loc}. Must be one of 'left', 'center', or 'right'.")
|
|
1247
|
+
if loc == "left":
|
|
1248
|
+
st["at"] = "{(xticklabel cs:0)}"
|
|
1249
|
+
st["anchor"] = "north west"
|
|
1250
|
+
elif loc == "right":
|
|
1251
|
+
st["at"] = "{(xticklabel cs:1)}"
|
|
1252
|
+
st["anchor"] = "north east"
|
|
1253
|
+
else:
|
|
1254
|
+
st["at"] = {}
|
|
1255
|
+
st["anchor"] = {}
|
|
1256
|
+
if "rotate" in kwargs:
|
|
1257
|
+
if kwargs["rotate"] not in ["vertical", "horizontal"]:
|
|
1258
|
+
raise Warning(f"Invalid rotate: {kwargs['rotate']}. Must be one of 'vertical' or 'horizontal'.")
|
|
1259
|
+
if kwargs["rotate"] == "vertical":
|
|
1260
|
+
st["rotate"] = "-90"
|
|
1261
|
+
else:
|
|
1262
|
+
st["rotate"] = {}
|
|
1263
|
+
if st:
|
|
1264
|
+
self._update_axis_options("x label style", st)
|
|
1006
1265
|
self._axis_options["xlabel"] = f"{{{tex_text(label)}}}"
|
|
1007
1266
|
|
|
1008
|
-
def set_title(self, title):
|
|
1267
|
+
def set_title(self, title, **kwargs):
|
|
1268
|
+
kws = {"fontsize", "color", "c", "loc"}
|
|
1269
|
+
kwargs = self._check_kwargs("set_title", kws, **kwargs)
|
|
1270
|
+
st = {}
|
|
1271
|
+
if "fontsize" in kwargs:
|
|
1272
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
1273
|
+
if "color" in kwargs or "c" in kwargs:
|
|
1274
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
1275
|
+
st["text"] = self._match_color(c)
|
|
1276
|
+
if "loc" in kwargs:
|
|
1277
|
+
loc = kwargs["loc"]
|
|
1278
|
+
if loc not in ["left", "center", "right"]:
|
|
1279
|
+
raise Warning(f"Invalid loc: {loc}. Must be one of 'left', 'center', or 'right'.")
|
|
1280
|
+
if loc == "left":
|
|
1281
|
+
st["at"] = "{(0.0,1.0)}"
|
|
1282
|
+
st["anchor"] = "south west"
|
|
1283
|
+
elif loc == "right":
|
|
1284
|
+
st["at"] = "{(1.0,1.0)}"
|
|
1285
|
+
st["anchor"] = "south east"
|
|
1286
|
+
else:
|
|
1287
|
+
st["at"] = {}
|
|
1288
|
+
st["anchor"] = {}
|
|
1289
|
+
if st:
|
|
1290
|
+
self._update_axis_options("title style", st)
|
|
1009
1291
|
self._axis_options["title"] = f"{{{tex_text(title)}}}"
|
|
1010
1292
|
|
|
1011
1293
|
def grid(self, visible=True, which="major", **kwargs):
|
|
@@ -1070,7 +1352,17 @@ class Axes(BaseAxes):
|
|
|
1070
1352
|
if "base" in kwargs:
|
|
1071
1353
|
self._axis_options["log basis x"] = kwargs["base"]
|
|
1072
1354
|
|
|
1073
|
-
def set_xticks(self, ticks, labels=None):
|
|
1355
|
+
def set_xticks(self, ticks, labels=None, **kwargs):
|
|
1356
|
+
kws = {"color", "c", "fontsize"}
|
|
1357
|
+
kwargs = self._check_kwargs("set_xticks", kws, **kwargs)
|
|
1358
|
+
st = {}
|
|
1359
|
+
if "color" in kwargs or "c" in kwargs:
|
|
1360
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
1361
|
+
st["text"] = self._match_color(c)
|
|
1362
|
+
if "fontsize" in kwargs:
|
|
1363
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
1364
|
+
if st:
|
|
1365
|
+
self._update_axis_options("x tick style", st)
|
|
1074
1366
|
if ticks:
|
|
1075
1367
|
s_ticks = map(str, ticks)
|
|
1076
1368
|
self._axis_options["xtick"]=f"{{{','.join(s_ticks)}}}"
|
|
@@ -1083,7 +1375,17 @@ class Axes(BaseAxes):
|
|
|
1083
1375
|
self._axis_options["xticks"]=r"{}"
|
|
1084
1376
|
self._xticks = False
|
|
1085
1377
|
|
|
1086
|
-
def set_xticklabels(self, labels):
|
|
1378
|
+
def set_xticklabels(self, labels, **kwargs):
|
|
1379
|
+
kws = {"color", "c", "fontsize"}
|
|
1380
|
+
kwargs = self._check_kwargs("set_xticklabels", kws, **kwargs)
|
|
1381
|
+
st = {}
|
|
1382
|
+
if "color" in kwargs or "c" in kwargs:
|
|
1383
|
+
c = kwargs.get("color", kwargs.get("c", None))
|
|
1384
|
+
st["text"] = self._match_color(c)
|
|
1385
|
+
if "fontsize" in kwargs:
|
|
1386
|
+
st["font"] = self._tex_fontsize(kwargs["fontsize"])
|
|
1387
|
+
if st:
|
|
1388
|
+
self._update_axis_options("x tick label style", st)
|
|
1087
1389
|
if labels:
|
|
1088
1390
|
self._axis_options["xticklabels"]=f"{{{tex_text(','.join(labels))}}}"
|
|
1089
1391
|
else:
|
|
@@ -1095,6 +1397,7 @@ class Axes(BaseAxes):
|
|
|
1095
1397
|
raise Exception("Cannot create twinx() on polar plot.")
|
|
1096
1398
|
self._secondary_y = Secondary(self)
|
|
1097
1399
|
self._ext_xmin = self._ext_xmax = True
|
|
1400
|
+
self.tick_params(axis="y", right=False)
|
|
1098
1401
|
return self._secondary_y
|
|
1099
1402
|
|
|
1100
1403
|
def _export_imshow(self, *args, **kwargs):
|
|
@@ -1195,16 +1498,12 @@ class Axes(BaseAxes):
|
|
|
1195
1498
|
if self._axis_options:
|
|
1196
1499
|
if axis_opt_str: axis_opt_str += ",\n"
|
|
1197
1500
|
if auxiliary_opt_str: auxiliary_opt_str += ",\n"
|
|
1198
|
-
def parse_entry(k,v):
|
|
1199
|
-
if isinstance(v, dict):
|
|
1200
|
-
return f"{k}={{" + ",\n".join(f"{kk}={vv}" for kk, vv in v.items()) + "}"
|
|
1201
|
-
else:
|
|
1202
|
-
return f"{k}={v}"
|
|
1203
1501
|
for k, v in self._axis_options.items():
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1502
|
+
if v != {}:
|
|
1503
|
+
entry = self._parse_entry(k,v)
|
|
1504
|
+
axis_opt_str += entry + ",\n"
|
|
1505
|
+
if k in ["xmin", "xmax", "ymin", "ymax", "xmode", "ymode", "log basis x", "log basis y", "width", "height", "at"]:
|
|
1506
|
+
auxiliary_opt_str += entry + ",\n"
|
|
1208
1507
|
axis_opt_str = axis_opt_str.removesuffix(",,\n")
|
|
1209
1508
|
auxiliary_opt_str = auxiliary_opt_str.removesuffix(",,\n")
|
|
1210
1509
|
if self._colorbar:
|
|
@@ -1213,6 +1512,12 @@ class Axes(BaseAxes):
|
|
|
1213
1512
|
axis_opt_str += f"colormap={self._cmap_bar._generate_tex_colormap(self._cmap_bar._cmap)},\n"
|
|
1214
1513
|
return axis_opt_str, "hide axis,\n" + auxiliary_opt_str, alias
|
|
1215
1514
|
|
|
1515
|
+
def _parse_entry(self, k, v):
|
|
1516
|
+
if v is None:
|
|
1517
|
+
return f"{k}"
|
|
1518
|
+
if isinstance(v, dict):
|
|
1519
|
+
return f"{k}={{" + ",\n".join(f"{kk}={vv}" for kk, vv in v.items() if vv != {}) + "}"
|
|
1520
|
+
return f"{k}={v}"
|
|
1216
1521
|
|
|
1217
1522
|
def _margins(self):
|
|
1218
1523
|
left = TikzConfig.LEFT_PADDING + TikzConfig.YTICK_PADDING * self._yticks + TikzConfig.Y_LABEL_PADDING * ("ylabel" in self._axis_options)
|
|
@@ -1242,6 +1547,27 @@ class Axes(BaseAxes):
|
|
|
1242
1547
|
return self._index
|
|
1243
1548
|
|
|
1244
1549
|
def _to_tex(self, filename, single):
|
|
1550
|
+
for k,v in self._elements.items():
|
|
1551
|
+
if k in self._bar_labels and len(self._bar_labels[k]) > 0:
|
|
1552
|
+
if k in self._overlay_special and ("xbar stacked" in self._overlay_special[k] or "ybar stacked" in self._overlay_special[k]):
|
|
1553
|
+
self._overlay_special[k] += ",\nnodes near coords"
|
|
1554
|
+
for e in self._elements[k].copy():
|
|
1555
|
+
if e not in self._bar_labels[k]:
|
|
1556
|
+
self.bar_label(e, labels=[])
|
|
1557
|
+
for e in self._elements[k]:
|
|
1558
|
+
self._bar_labels[k][e]["stacked"] = True
|
|
1559
|
+
for e in self._bar_labels[k]:
|
|
1560
|
+
e._add_bar_labels(**self._bar_labels[k][e])
|
|
1561
|
+
"""if isinstance(v, list) and len(v) > 1:
|
|
1562
|
+
reference = getattr(v[0], "_settings", {}) or {}
|
|
1563
|
+
common_style = {s: val for s, val in reference.items() if all((getattr(obj, "_settings", {}) or {}).get(s) == val for obj in v[1:])}
|
|
1564
|
+
forbiden = {"draw", "fill", "xbar", "ybar"}
|
|
1565
|
+
for f in forbiden:
|
|
1566
|
+
common_style.pop(f, None)
|
|
1567
|
+
for key, val in common_style.items():
|
|
1568
|
+
for e in v:
|
|
1569
|
+
e._settings.pop(key, None)
|
|
1570
|
+
self._overlay_special[k] += f",\n{self._parse_entry(key, val)}" """
|
|
1245
1571
|
lines = []
|
|
1246
1572
|
lines2 = []
|
|
1247
1573
|
if self._polar:
|
|
@@ -1341,14 +1667,14 @@ class Secondary(BaseAxes):
|
|
|
1341
1667
|
self._axis_options["axis x line"] = "none"
|
|
1342
1668
|
self._axis_options["at"] = f"{{({primary._axis_options['alias' if 'alias' in primary._axis_options else 'name']}.south west)}}"
|
|
1343
1669
|
self._axis_options["anchor"] = "south west"
|
|
1344
|
-
self.
|
|
1670
|
+
self._update_axis_options("y label style", {"at": f"{{({TikzConfig.SEC_YLABEL_LOC[0]},{TikzConfig.SEC_YLABEL_LOC[1]})}}", "rotate": 180})
|
|
1345
1671
|
|
|
1346
1672
|
self._fig = primary._fig
|
|
1347
1673
|
self._style = self._fig._style
|
|
1348
1674
|
self._style_defaults()
|
|
1349
1675
|
|
|
1350
1676
|
def _style_defaults(self):
|
|
1351
|
-
_add_settgs = self._style._get_additional_settings()
|
|
1677
|
+
_add_settgs = copy.deepcopy(self._style._get_additional_settings())
|
|
1352
1678
|
if _add_settgs is not None:
|
|
1353
1679
|
self._axis_options = _add_settgs | self._axis_options
|
|
1354
1680
|
|