myplot 0.1.0__py3-none-any.whl
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.
- myplot/README.md +98 -0
- myplot/__init__.py +427 -0
- myplot-0.1.0.dist-info/METADATA +78 -0
- myplot-0.1.0.dist-info/RECORD +5 -0
- myplot-0.1.0.dist-info/WHEEL +4 -0
myplot/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# MyPlot - Uniform Plotting Module for Matplotlib
|
|
2
|
+
|
|
3
|
+
A Python module for creating publication-quality matplotlib plots with automatic font detection and consistent formatting.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automatic Font Detection**: Searches for Source Han Serif font across system paths and project directories
|
|
8
|
+
- **Cross-platform Support**: Works on Windows, macOS, and Linux
|
|
9
|
+
- **Consistent Formatting**: Pre-configured plot styling parameters
|
|
10
|
+
- **Flexible Configuration**: Platform-specific configuration directories
|
|
11
|
+
- **Utility Functions**: Formatters for radians, degrees, and dB values
|
|
12
|
+
- **Easy Integration**: Simple decorator for creating plots
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Install dependencies
|
|
18
|
+
pip install matplotlib numpy
|
|
19
|
+
|
|
20
|
+
# For development/testing (optional)
|
|
21
|
+
pip install hatch
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic Plotting
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from myplot import myplot, plt, np
|
|
30
|
+
|
|
31
|
+
@myplot()
|
|
32
|
+
def my_function():
|
|
33
|
+
x = np.linspace(0, 10, 100)
|
|
34
|
+
y = np.sin(x)
|
|
35
|
+
plt.plot(x, y)
|
|
36
|
+
plt.xlabel('x')
|
|
37
|
+
plt.ylabel('sin(x)')
|
|
38
|
+
plt.title('Sine Wave')
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Custom Figure Size and Save Options
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
@myplot(figsize=(8, 6), save=True, log=True)
|
|
45
|
+
def custom_plot():
|
|
46
|
+
# Your plotting code here
|
|
47
|
+
pass
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Utilities
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from myplot import myUtilities
|
|
54
|
+
|
|
55
|
+
# Use custom formatters
|
|
56
|
+
ax.xaxis.set_major_formatter(myUtilities.radFormatter())
|
|
57
|
+
ax.xaxis.set_major_formatter(myUtilities.degFormatter())
|
|
58
|
+
ax.xaxis.set_major_formatter(myUtilities.dBFormatter())
|
|
59
|
+
|
|
60
|
+
# Use custom locator
|
|
61
|
+
ax.xaxis.set_major_locator(myUtilities.radLocator())
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Configuration
|
|
65
|
+
|
|
66
|
+
The module uses platform-specific configuration directories:
|
|
67
|
+
- **Windows**: `%USERPROFILE%\AppData\Local\myplot\`
|
|
68
|
+
- **macOS**: `~/Library/Application Support/myplot/`
|
|
69
|
+
- **Linux**: `~/.config/myplot/`
|
|
70
|
+
|
|
71
|
+
## Font Detection
|
|
72
|
+
|
|
73
|
+
The module automatically searches for Source Han Serif font in:
|
|
74
|
+
- System font directories
|
|
75
|
+
- User font directories
|
|
76
|
+
- Project local directories (fonts/, resources/fonts/, src/fonts/)
|
|
77
|
+
- Current directory
|
|
78
|
+
|
|
79
|
+
## Testing
|
|
80
|
+
|
|
81
|
+
Run the test suite:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
python minimal_test.py
|
|
85
|
+
python test_myplot.py
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
To install in development mode:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install -e .
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT License
|
myplot/__init__.py
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MyPlot - A uniform plotting module for matplotlib with proper font handling.
|
|
3
|
+
|
|
4
|
+
This module provides decorators and utilities for creating publication-quality plots
|
|
5
|
+
with consistent formatting and automatic font detection.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import platform
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import matplotlib.font_manager as fm
|
|
11
|
+
import matplotlib.ticker as ticker
|
|
12
|
+
import numpy as np
|
|
13
|
+
import functools
|
|
14
|
+
import sys
|
|
15
|
+
import builtins
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
__all__ = ['myplot', 'plt', 'np', 'myparams', 'myUtilities']
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class myparams:
|
|
24
|
+
"""Configuration parameter manager with JSON storage."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, file="myplot_params.json", config_dir=None):
|
|
27
|
+
if config_dir:
|
|
28
|
+
config_path = Path(config_dir)
|
|
29
|
+
else:
|
|
30
|
+
# Use platform-specific configuration directories
|
|
31
|
+
if platform.system() == 'Windows':
|
|
32
|
+
config_path = Path.home() / 'AppData' / 'Local' / 'myplot'
|
|
33
|
+
elif platform.system() == 'Darwin': # macOS
|
|
34
|
+
config_path = Path.home() / 'Library' / 'Application Support' / 'myplot'
|
|
35
|
+
else: # Linux and other Unix-like systems
|
|
36
|
+
config_path = Path.home() / '.config' / 'myplot'
|
|
37
|
+
|
|
38
|
+
self.f = config_path / file
|
|
39
|
+
self.f.parent.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
with open(self.f, 'r') as ff:
|
|
43
|
+
self.p = json.load(ff)
|
|
44
|
+
except (FileNotFoundError, json.JSONDecodeError):
|
|
45
|
+
self.p = {}
|
|
46
|
+
|
|
47
|
+
def get(self, key):
|
|
48
|
+
return self.p.get(key)
|
|
49
|
+
|
|
50
|
+
def put(self, arg: dict = None, **kwargs):
|
|
51
|
+
d = (arg or {}) | (kwargs or {})
|
|
52
|
+
for k in d:
|
|
53
|
+
self.p[k] = d[k]
|
|
54
|
+
self.update()
|
|
55
|
+
|
|
56
|
+
def update(self):
|
|
57
|
+
with open(self.f, 'w') as ff:
|
|
58
|
+
json.dump(self.p, ff, indent=4)
|
|
59
|
+
|
|
60
|
+
def __getitem__(self, key):
|
|
61
|
+
return self.get(key)
|
|
62
|
+
|
|
63
|
+
def __setitem__(self, key, value):
|
|
64
|
+
self.put({key: value})
|
|
65
|
+
|
|
66
|
+
def __delitem__(self, key):
|
|
67
|
+
if key in self.p:
|
|
68
|
+
del self.p[key]
|
|
69
|
+
self.update()
|
|
70
|
+
|
|
71
|
+
def __contains__(self, key):
|
|
72
|
+
return key in self.p
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_font_search_paths():
|
|
76
|
+
"""Get platform-specific font search paths."""
|
|
77
|
+
system = platform.system()
|
|
78
|
+
paths = []
|
|
79
|
+
|
|
80
|
+
# Add system font paths
|
|
81
|
+
if system == 'Windows':
|
|
82
|
+
system_font_dirs = [
|
|
83
|
+
Path('C:/Windows/Fonts'),
|
|
84
|
+
Path('C:/Windows/Fonts/simhei'), # Chinese fonts
|
|
85
|
+
]
|
|
86
|
+
if 'LOCALAPPDATA' in os.environ:
|
|
87
|
+
user_font_dir = Path(os.environ['LOCALAPPDATA']) / 'Microsoft' / 'Windows' / 'Fonts'
|
|
88
|
+
if user_font_dir.exists():
|
|
89
|
+
paths.append(user_font_dir)
|
|
90
|
+
|
|
91
|
+
elif system == 'Darwin': # macOS
|
|
92
|
+
system_font_dirs = [
|
|
93
|
+
Path('/System/Library/Fonts'),
|
|
94
|
+
Path('/System/Library/Fonts/ChineseSimplified'),
|
|
95
|
+
Path('/Library/Fonts'),
|
|
96
|
+
Path('/Library/Fonts/ChineseSimplified'),
|
|
97
|
+
]
|
|
98
|
+
user_font_dir = Path.home() / 'Library' / 'Fonts'
|
|
99
|
+
if user_font_dir.exists():
|
|
100
|
+
paths.append(user_font_dir)
|
|
101
|
+
else: # Linux
|
|
102
|
+
system_font_dirs = [
|
|
103
|
+
Path('/usr/share/fonts'),
|
|
104
|
+
Path('/usr/share/fonts/truetype'),
|
|
105
|
+
Path('/usr/share/fonts/opentype'),
|
|
106
|
+
Path('/usr/share/fonts/truetype/freefont'),
|
|
107
|
+
Path('/usr/share/fonts/opentype/noto'),
|
|
108
|
+
]
|
|
109
|
+
user_font_dir = Path.home() / '.local' / 'share' / 'fonts'
|
|
110
|
+
if user_font_dir.exists():
|
|
111
|
+
paths.append(user_font_dir)
|
|
112
|
+
|
|
113
|
+
# Add existing system font directories
|
|
114
|
+
for font_dir in system_font_dirs:
|
|
115
|
+
if font_dir.exists():
|
|
116
|
+
paths.append(font_dir)
|
|
117
|
+
|
|
118
|
+
# Add current directory and relative paths (for local font files)
|
|
119
|
+
current_dir = Path.cwd()
|
|
120
|
+
paths.extend([
|
|
121
|
+
current_dir,
|
|
122
|
+
current_dir / 'fonts',
|
|
123
|
+
current_dir / 'resources' / 'fonts',
|
|
124
|
+
current_dir / 'src' / 'fonts',
|
|
125
|
+
])
|
|
126
|
+
|
|
127
|
+
return paths
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def find_source_han_serif_font():
|
|
131
|
+
"""Find Source Han Serif font across multiple search paths."""
|
|
132
|
+
font_names_variants = [
|
|
133
|
+
'SourceHanSerifSC-Medium.otf',
|
|
134
|
+
'SourceHanSerif-Medium.otf',
|
|
135
|
+
'SourceHanSerifSC-Regular.otf',
|
|
136
|
+
'SourceHanSerif-Regular.otf',
|
|
137
|
+
'SourceHanSerif-Heavy.otf',
|
|
138
|
+
'NotoSerifCJK-Regular.otf', # Alternative
|
|
139
|
+
'NotoSerifCJK-Medium.otf', # Alternative
|
|
140
|
+
'NotoSerifCJK-SC-Regular.otf',
|
|
141
|
+
'NotoSerifCJK-TC-Regular.otf'
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
# Search in all possible paths
|
|
145
|
+
search_paths = get_font_search_paths()
|
|
146
|
+
|
|
147
|
+
print(f"Searching in {len(search_paths)} paths:")
|
|
148
|
+
for i, path in enumerate(search_paths, 1):
|
|
149
|
+
print(f" {i}. {path}")
|
|
150
|
+
|
|
151
|
+
# First, try to find font files in the directory
|
|
152
|
+
for font_variant in font_names_variants:
|
|
153
|
+
for search_path in search_paths:
|
|
154
|
+
font_path = search_path / font_variant
|
|
155
|
+
if font_path.exists():
|
|
156
|
+
print(f"Found font file: {font_path}")
|
|
157
|
+
return font_path.resolve()
|
|
158
|
+
|
|
159
|
+
# Second, check matplotlib's known fonts
|
|
160
|
+
print("\nChecking matplotlib's known fonts...")
|
|
161
|
+
font_list = fm.findSystemFonts()
|
|
162
|
+
for font_file in font_list:
|
|
163
|
+
font_name = Path(font_file).name
|
|
164
|
+
for font_variant in font_names_variants:
|
|
165
|
+
if font_variant.lower() in font_name.lower():
|
|
166
|
+
font_path = Path(font_file).resolve()
|
|
167
|
+
print(f"Found font in system: {font_path}")
|
|
168
|
+
return font_path
|
|
169
|
+
|
|
170
|
+
# Check if any font variant is already registered
|
|
171
|
+
print("\nChecking registered fonts...")
|
|
172
|
+
try:
|
|
173
|
+
for font_variant in font_names_variants:
|
|
174
|
+
font_list = fm.findSystemFonts()
|
|
175
|
+
for font_file in font_list:
|
|
176
|
+
try:
|
|
177
|
+
font_obj = fm.FontProperties(fname=font_file)
|
|
178
|
+
font_name = font_obj.get_name()
|
|
179
|
+
if ('source' in font_name.lower() and 'serif' in font_name.lower()) or \
|
|
180
|
+
font_variant.replace('.otf', '') in font_name.replace(' ', ''):
|
|
181
|
+
font_path = Path(font_file).resolve()
|
|
182
|
+
print(f"Found matching font: {font_path}")
|
|
183
|
+
print(f" Font name: {font_name}")
|
|
184
|
+
return font_path
|
|
185
|
+
except Exception:
|
|
186
|
+
continue
|
|
187
|
+
except Exception as e:
|
|
188
|
+
print(f"Error checking fonts: {e}")
|
|
189
|
+
|
|
190
|
+
print('⚠ Cannot find Source Han Serif font, using default settings')
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def setup_font_properties():
|
|
195
|
+
"""Setup matplotlib font properties if Source Han Serif is found."""
|
|
196
|
+
font_path = find_source_han_serif_font()
|
|
197
|
+
|
|
198
|
+
if font_path:
|
|
199
|
+
try:
|
|
200
|
+
# Force register the font
|
|
201
|
+
fontManager = fm.fontManager
|
|
202
|
+
fontManager.addfont(str(font_path))
|
|
203
|
+
|
|
204
|
+
# Create font properties
|
|
205
|
+
font_prop = fm.FontProperties(fname=str(font_path))
|
|
206
|
+
font_name = font_prop.get_name()
|
|
207
|
+
print(f"Font file found: {font_path}")
|
|
208
|
+
print(f"Font name detected: {font_name}")
|
|
209
|
+
|
|
210
|
+
# Try multiple possible family names
|
|
211
|
+
possible_families = [
|
|
212
|
+
'Source Han Serif',
|
|
213
|
+
'Source Han Serif SC',
|
|
214
|
+
'Source Han Serif TC',
|
|
215
|
+
'Noto Serif CJK SC',
|
|
216
|
+
'Noto Serif CJK'
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
# Set matplotlib font configuration
|
|
220
|
+
plt.rcParams['mathtext.fontset'] = 'stix'
|
|
221
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
222
|
+
plt.rcParams['font.family'] = 'sans-serif' # fallback
|
|
223
|
+
plt.rcParams['font.sans-serif'] = ['Source Han Serif'] + plt.rcParams['font.sans-serif']
|
|
224
|
+
|
|
225
|
+
# Manually set the font for the current session
|
|
226
|
+
try:
|
|
227
|
+
# Try to find the correct family name from the registered font
|
|
228
|
+
for family in possible_families:
|
|
229
|
+
if family in fontManager.get_font_names():
|
|
230
|
+
plt.rcParams['font.family'] = family
|
|
231
|
+
plt.rcParams['font.sans-serif'] = [family] + plt.rcParams['font.sans-serif']
|
|
232
|
+
print(f"Set font family to: {family}")
|
|
233
|
+
break
|
|
234
|
+
except Exception as e:
|
|
235
|
+
print(f"Note: Could not set font family explicitly: {e}")
|
|
236
|
+
|
|
237
|
+
return True
|
|
238
|
+
except Exception as e:
|
|
239
|
+
print(f"Error setting up font: {e}")
|
|
240
|
+
return False
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Initialize font setup
|
|
245
|
+
setup_successful = setup_font_properties()
|
|
246
|
+
myparams.font = None
|
|
247
|
+
|
|
248
|
+
# Load the font into myparams if setup was successful
|
|
249
|
+
if setup_successful:
|
|
250
|
+
font_path = find_source_han_serif_font()
|
|
251
|
+
if font_path:
|
|
252
|
+
try:
|
|
253
|
+
myparams.font = fm.FontProperties(fname=str(font_path))
|
|
254
|
+
print("Font loaded into myparams")
|
|
255
|
+
except Exception as e:
|
|
256
|
+
print(f"Could not load font into myparams: {e}")
|
|
257
|
+
|
|
258
|
+
# Character size settings
|
|
259
|
+
plt.rcParams['font.size'] = 9
|
|
260
|
+
plt.rcParams['axes.titlesize'] = 11
|
|
261
|
+
plt.rcParams['axes.labelsize'] = 10
|
|
262
|
+
plt.rcParams['xtick.labelsize'] = 9
|
|
263
|
+
plt.rcParams['ytick.labelsize'] = 9
|
|
264
|
+
plt.rcParams['lines.linewidth'] = 1.2
|
|
265
|
+
plt.rcParams['lines.markersize'] = 4
|
|
266
|
+
plt.rcParams['errorbar.capsize'] = 3
|
|
267
|
+
elinewidth = 0.8
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def myplot(figsize=(4.8, 3.6), save=True, log=True, savename='', config_dir=None):
|
|
271
|
+
"""
|
|
272
|
+
Decorator for creating uniformly formatted matplotlib plots.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
figsize: Figure size in inches (width, height)
|
|
276
|
+
save: Whether to save the figure
|
|
277
|
+
log: Whether to log plot creation
|
|
278
|
+
savename: Filename for saved figure (defaults to function name)
|
|
279
|
+
config_dir: Directory for configuration files (optional)
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Decorator function
|
|
283
|
+
"""
|
|
284
|
+
def decorator(func):
|
|
285
|
+
@functools.wraps(func)
|
|
286
|
+
def wrap(*args, **kwargs):
|
|
287
|
+
# Process keyword arguments
|
|
288
|
+
recfg_list = ['figsize', 'save', 'log', 'savename', 'config_dir']
|
|
289
|
+
p = {}
|
|
290
|
+
for rec in recfg_list:
|
|
291
|
+
if rec in kwargs:
|
|
292
|
+
p[rec] = kwargs[rec]
|
|
293
|
+
del kwargs[rec]
|
|
294
|
+
else:
|
|
295
|
+
p[rec] = {
|
|
296
|
+
'figsize': figsize,
|
|
297
|
+
'save': save,
|
|
298
|
+
'log': log,
|
|
299
|
+
'savename': savename or func.__name__,
|
|
300
|
+
'config_dir': config_dir
|
|
301
|
+
}.get(rec)
|
|
302
|
+
|
|
303
|
+
# Setup plot environment
|
|
304
|
+
plt.figure(figsize=p['figsize'])
|
|
305
|
+
biprint = builtins.print
|
|
306
|
+
|
|
307
|
+
# Setup logging and save paths
|
|
308
|
+
if p['log'] or p['save']:
|
|
309
|
+
# Determine save directories
|
|
310
|
+
if p['config_dir']:
|
|
311
|
+
base_path = Path(p['config_dir'])
|
|
312
|
+
else:
|
|
313
|
+
# Use platform-specific directories
|
|
314
|
+
if platform.system() == 'Windows':
|
|
315
|
+
base_path = Path.home() / 'Documents' / 'MyPlots'
|
|
316
|
+
elif platform.system() == 'Darwin': # macOS
|
|
317
|
+
base_path = Path.home() / 'Documents' / 'MyPlots'
|
|
318
|
+
else: # Linux
|
|
319
|
+
base_path = Path.home() / 'Documents' / 'MyPlots'
|
|
320
|
+
|
|
321
|
+
stats_dir = base_path / 'stats'
|
|
322
|
+
imags_dir = base_path / 'images'
|
|
323
|
+
|
|
324
|
+
stats_dir.mkdir(parents=True, exist_ok=True)
|
|
325
|
+
imags_dir.mkdir(parents=True, exist_ok=True)
|
|
326
|
+
|
|
327
|
+
if p['log'] and p['save']:
|
|
328
|
+
logpath = stats_dir / f"{p['savename']}.txt"
|
|
329
|
+
with open(logpath, 'w') as f:
|
|
330
|
+
pass
|
|
331
|
+
biprint(f"{p['savename']} created stats into path:\n{logpath.absolute()}")
|
|
332
|
+
builtins.print = myprint(logpath)(biprint)
|
|
333
|
+
|
|
334
|
+
# Call parent function
|
|
335
|
+
result = func(*args, **kwargs)
|
|
336
|
+
|
|
337
|
+
# Cleanup
|
|
338
|
+
plt.tight_layout()
|
|
339
|
+
builtins.print = biprint
|
|
340
|
+
if p['save']:
|
|
341
|
+
pltpath = imags_dir / f"{p['savename']}.png"
|
|
342
|
+
pltpath.parent.mkdir(parents=True, exist_ok=True)
|
|
343
|
+
plt.savefig(pltpath, dpi=400, bbox_inches='tight')
|
|
344
|
+
print(f"{p['savename']} saved figure into path:\n{pltpath.absolute()}")
|
|
345
|
+
plt.show()
|
|
346
|
+
return result
|
|
347
|
+
|
|
348
|
+
return wrap
|
|
349
|
+
return decorator
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def myprint(target):
|
|
353
|
+
"""Decorator for redirecting print output to a file."""
|
|
354
|
+
def decorator(func):
|
|
355
|
+
def wrap(*args, **kwargs):
|
|
356
|
+
temp = sys.stdout
|
|
357
|
+
try:
|
|
358
|
+
with open(target, 'a') as f:
|
|
359
|
+
sys.stdout = f
|
|
360
|
+
func(*args, **kwargs)
|
|
361
|
+
finally:
|
|
362
|
+
sys.stdout = temp
|
|
363
|
+
return wrap
|
|
364
|
+
return decorator
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# Override errorbar with custom line width
|
|
368
|
+
def _myerrorbar(errorbar):
|
|
369
|
+
def wrap(*args, **kwargs):
|
|
370
|
+
if 'elinewidth' not in kwargs:
|
|
371
|
+
kwargs['elinewidth'] = elinewidth
|
|
372
|
+
return errorbar(*args, **kwargs)
|
|
373
|
+
return wrap
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
plt.errorbar = _myerrorbar(plt.errorbar)
|
|
377
|
+
plt.Axes.errorbar = _myerrorbar(plt.Axes.errorbar)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
class myUtilities:
|
|
381
|
+
"""Utility functions for plot formatting."""
|
|
382
|
+
|
|
383
|
+
@staticmethod
|
|
384
|
+
def radFormatter(dig='.2f'):
|
|
385
|
+
"""Create radian tick formatter (e.g., 0.5π)."""
|
|
386
|
+
return ticker.FuncFormatter(lambda x, pos: f"{x / np.pi:{dig}}π")
|
|
387
|
+
|
|
388
|
+
@staticmethod
|
|
389
|
+
def degFormatter(dig='1.0f'):
|
|
390
|
+
"""Create degree tick formatter (e.g., 90°)."""
|
|
391
|
+
return ticker.FuncFormatter(lambda x, pos: f"{x:{dig}}°")
|
|
392
|
+
|
|
393
|
+
@staticmethod
|
|
394
|
+
def dBFormatter(dig='1.2f'):
|
|
395
|
+
"""Create dB tick formatter."""
|
|
396
|
+
return ticker.FuncFormatter(lambda x, pos: f"{x:{dig}} dB")
|
|
397
|
+
|
|
398
|
+
@staticmethod
|
|
399
|
+
def radLocator(base=np.pi / 4):
|
|
400
|
+
"""Create radian tick locator."""
|
|
401
|
+
return ticker.MultipleLocator(base=base)
|
|
402
|
+
|
|
403
|
+
@staticmethod
|
|
404
|
+
def get_available_fonts():
|
|
405
|
+
"""Get list of available fonts."""
|
|
406
|
+
return fm.findSystemFonts()
|
|
407
|
+
|
|
408
|
+
@staticmethod
|
|
409
|
+
def get_font_properties(font_path):
|
|
410
|
+
"""Get font properties from font file."""
|
|
411
|
+
return fm.FontProperties(fname=str(font_path))
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
# Examples for documentation
|
|
415
|
+
if __name__ == "__main__":
|
|
416
|
+
# Test the module
|
|
417
|
+
print(f"Platform: {platform.system()}")
|
|
418
|
+
print(f"Font setup successful: {setup_successful}")
|
|
419
|
+
|
|
420
|
+
# List some available fonts
|
|
421
|
+
available_fonts = myUtilities.get_available_fonts()
|
|
422
|
+
print(f"Found {len(available_fonts)} fonts")
|
|
423
|
+
|
|
424
|
+
# Test font finding
|
|
425
|
+
source_han_font = find_source_han_serif_font()
|
|
426
|
+
if source_han_font:
|
|
427
|
+
print(f"Source Han Serif found at: {source_han_font}")
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: myplot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A uniform plotting module for matplotlib with automatic font detection
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
7
|
+
Requires-Dist: numpy>=1.20.0
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# MyPlot
|
|
11
|
+
|
|
12
|
+
A uniform plotting module for matplotlib with automatic font detection and cross-platform support.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Uniform Formatting**: Consistent plot styling across all your projects
|
|
17
|
+
- **Automatic Font Detection**: Finds and configures Source Han Serif font for Chinese characters
|
|
18
|
+
- **Cross-Platform Support**: Works on Windows, macOS, and Linux
|
|
19
|
+
- **Easy to Use**: Simple decorator interface for creating publication-quality plots
|
|
20
|
+
- **Built-in Utilities**: Radian/degree formatters and other plotting utilities
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install -e .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from myplot import myplot, plt, np
|
|
32
|
+
|
|
33
|
+
@myplot()
|
|
34
|
+
def simple_plot():
|
|
35
|
+
x = np.linspace(0, 10, 100)
|
|
36
|
+
plt.plot(x, np.sin(x))
|
|
37
|
+
plt.xlabel('x')
|
|
38
|
+
plt.ylabel('sin(x)')
|
|
39
|
+
plt.title('Simple Plot')
|
|
40
|
+
|
|
41
|
+
simple_plot()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Advanced Usage
|
|
45
|
+
|
|
46
|
+
### Custom Figure Size
|
|
47
|
+
```python
|
|
48
|
+
@myplot(figsize=(8, 6))
|
|
49
|
+
def large_plot():
|
|
50
|
+
# Your plotting code here
|
|
51
|
+
pass
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Save Plots Automatically
|
|
55
|
+
```python
|
|
56
|
+
@myplot(save=True, log=True)
|
|
57
|
+
def save_plot():
|
|
58
|
+
# Plot will be saved automatically
|
|
59
|
+
pass
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Using Utilities
|
|
63
|
+
```python
|
|
64
|
+
from myplot import myUtilities
|
|
65
|
+
|
|
66
|
+
ax.xaxis.set_major_formatter(myUtilities.radFormatter())
|
|
67
|
+
ax.xaxis.set_major_locator(myUtilities.radLocator(np.pi/4))
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Requirements
|
|
71
|
+
|
|
72
|
+
- Python 3.8+
|
|
73
|
+
- matplotlib >= 3.5.0
|
|
74
|
+
- numpy >= 1.20.0
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT License
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
myplot/README.md,sha256=_lfv96hxr9egsEzmma1SieNolWcDQCrjIm90l3C4PR4,2152
|
|
2
|
+
myplot/__init__.py,sha256=hEohqksp6EjfFkWBn64J4TIE3eTHQIVQoF_bhyrh60U,14532
|
|
3
|
+
myplot-0.1.0.dist-info/METADATA,sha256=OzI1Y8dw-eUlv-bVya6y5E4spUoOJE0tX8uPVz1gRFw,1629
|
|
4
|
+
myplot-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
5
|
+
myplot-0.1.0.dist-info/RECORD,,
|