├── .devcontainer └── devcontainer.json ├── .github ├── dependabot.yml └── workflows │ ├── build_docs.yaml │ ├── python-publish.yml │ └── test.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── changelog.md ├── images │ └── zhplot_demo.png ├── index.md └── overrides │ └── main.html ├── mkdocs.yml ├── pyproject.toml ├── tests ├── test_matplotlib.py └── test_wordcloud.py ├── uv.lock └── zhplot ├── __init__.py ├── fonts ├── NotoSansSC-Regular.ttf ├── OFL.txt └── README.txt ├── matplotlib.py ├── py.typed └── wordcloud.py /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/python 3 | { 4 | "name": "ZhPlot - VS Code dev container", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/python:1-3.12", 7 | 8 | // Features to add to the dev container. More info: https://containers.dev/features. 9 | "features": { 10 | "ghcr.io/devcontainers-contrib/features/poetry:2": {} 11 | }, 12 | 13 | // Configure tool-specific properties. 14 | "customizations": { 15 | // Configure properties specific to VS Code. 16 | "vscode": { 17 | "settings": {}, 18 | "extensions": [ 19 | "streetsidesoftware.code-spell-checker" 20 | ] 21 | }, 22 | "codespaces": { 23 | "openFiles": [ 24 | "README.md", 25 | ] 26 | } 27 | }, 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | // "forwardPorts": [9000], 31 | 32 | // Use 'portsAttributes' to set default properties for specific forwarded ports. 33 | // More info: https://containers.dev/implementors/json_reference/#port-attributes 34 | // "portsAttributes": { 35 | // "9000": { 36 | // "label": "Hello Remote World", 37 | // "onAutoForward": "notify" 38 | // } 39 | // }, 40 | 41 | // Use 'postCreateCommand' to run commands after the container is created. 42 | "postCreateCommand": "poetry install" 43 | 44 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 45 | // "remoteUser": "root" 46 | } 47 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "pip" 13 | directory: "/" 14 | schedule: 15 | interval: "monthly" 16 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yaml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | name: Build docs (python-${{ matrix.os }}) 12 | strategy: 13 | matrix: 14 | os: 15 | - ubuntu-latest 16 | fail-fast: false 17 | runs-on: ${{ matrix.os }} 18 | env: 19 | UV_HTTP_TIMEOUT: 900 # max 15min to install deps 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Install uv 26 | uses: astral-sh/setup-uv@v6 27 | with: 28 | enable-cache: true 29 | cache-dependency-glob: "uv.lock" 30 | 31 | - name: "Set up Python" 32 | uses: actions/setup-python@v5 33 | with: 34 | python-version-file: "pyproject.toml" 35 | 36 | - name: Install the project 37 | run: uv sync --extra docs 38 | 39 | - name: Build docs 40 | run: uv run mkdocs build 41 | 42 | - name: Minimize uv cache 43 | run: uv cache prune --ci 44 | 45 | deploy: 46 | name: Deploy docs 47 | needs: build 48 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 49 | runs-on: ubuntu-latest 50 | env: 51 | UV_HTTP_TIMEOUT: 900 # max 15min to install deps 52 | steps: 53 | - uses: actions/checkout@v4 54 | with: 55 | fetch-depth: 0 56 | 57 | - name: Install uv 58 | uses: astral-sh/setup-uv@v6 59 | with: 60 | enable-cache: true 61 | cache-dependency-glob: "uv.lock" 62 | 63 | - name: "Set up Python" 64 | uses: actions/setup-python@v5 65 | with: 66 | python-version-file: "pyproject.toml" 67 | 68 | - name: Install the project 69 | run: uv sync --extra docs 70 | 71 | - name: Deploy docs 72 | run: uv run mkdocs gh-deploy --force 73 | 74 | - name: Minimize uv cache 75 | run: uv cache prune --ci 76 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | pypi-publish: 20 | name: upload release to PyPI 21 | runs-on: ubuntu-latest 22 | # Specifying a GitHub environment is optional, but strongly encouraged 23 | environment: pypi 24 | permissions: 25 | # IMPORTANT: this permission is mandatory for trusted publishing 26 | id-token: write 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Install uv 30 | uses: astral-sh/setup-uv@v6 31 | with: 32 | enable-cache: true 33 | cache-dependency-glob: "uv.lock" 34 | - name: "Set up Python" 35 | uses: actions/setup-python@v5 36 | with: 37 | python-version-file: "pyproject.toml" 38 | 39 | - name: Install the project 40 | run: uv sync --all-extras --dev 41 | - name: Build package 42 | run: uv build 43 | - name: Publish package distributions to PyPI 44 | uses: pypa/gh-action-pypi-publish@release/v1 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | ci: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | exclude: 17 | # TODO: re-include it 18 | # excludes python3.13 on windows due to error: Windows fatal exception: access violation 19 | - os: windows-latest 20 | python-version: "3.13" 21 | runs-on: ${{ matrix.os }} 22 | env: 23 | UV_HTTP_TIMEOUT: 900 # max 15min to install deps 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Install uv 28 | uses: astral-sh/setup-uv@v6 29 | with: 30 | enable-cache: true 31 | cache-dependency-glob: "uv.lock" 32 | 33 | - name: Set up Python 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: ${{ matrix.python-version }} 37 | 38 | - name: Install the project 39 | run: uv sync --all-extras --dev 40 | 41 | - name: Run tests and generate coverage report 42 | run: uv run pytest --doctest-modules -v --cov=zhplot --cov-fail-under 90 --cov-report=term --cov-report=xml --cov-report=html zhplot tests 43 | 44 | - name: Upload test results to Codecov 45 | if: success() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' 46 | uses: codecov/test-results-action@v1 47 | with: 48 | token: ${{ secrets.CODECOV_TOKEN }} 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ide cache 2 | .vscode 3 | .idea 4 | 5 | 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | build/ 18 | develop-eggs/ 19 | dist/ 20 | downloads/ 21 | eggs/ 22 | .eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | wheels/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | cover/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | db.sqlite3 68 | db.sqlite3-journal 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | .pybuilder/ 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | # For a library or package, you might want to ignore these files since the code is 93 | # intended to run in multiple environments; otherwise, check them in: 94 | # .python-version 95 | 96 | # pipenv 97 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 98 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 99 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 100 | # install all needed dependencies. 101 | #Pipfile.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/#use-with-ide 116 | .pdm.toml 117 | 118 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 119 | __pypackages__/ 120 | 121 | # Celery stuff 122 | celerybeat-schedule 123 | celerybeat.pid 124 | 125 | # SageMath parsed files 126 | *.sage.py 127 | 128 | # Environments 129 | .env 130 | .venv 131 | env/ 132 | venv/ 133 | ENV/ 134 | env.bak/ 135 | venv.bak/ 136 | 137 | # Spyder project settings 138 | .spyderproject 139 | .spyproject 140 | 141 | # Rope project settings 142 | .ropeproject 143 | 144 | # mkdocs documentation 145 | /site 146 | 147 | # mypy 148 | .mypy_cache/ 149 | .dmypy.json 150 | dmypy.json 151 | 152 | # Pyre type checker 153 | .pyre/ 154 | 155 | # pytype static type analyzer 156 | .pytype/ 157 | 158 | # Cython debug symbols 159 | cython_debug/ 160 | 161 | # PyCharm 162 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 163 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 164 | # and can be added to the global gitignore or merged into this file. For a more nuclear 165 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 166 | #.idea/ 167 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-toml 6 | - id: check-yaml 7 | - id: end-of-file-fixer 8 | - id: trailing-whitespace 9 | exclude: .+\.csv 10 | - id: mixed-line-ending 11 | args: [--fix=lf] 12 | - repo: https://github.com/pycqa/isort 13 | rev: 6.0.1 14 | hooks: 15 | - id: isort 16 | args: ["--profile", "black", "--filter-files"] 17 | - repo: https://github.com/astral-sh/ruff-pre-commit 18 | # Ruff version. 19 | rev: v0.11.12 20 | hooks: 21 | # Run the linter. 22 | - id: ruff 23 | # Run the formatter. 24 | - id: ruff-format 25 | - repo: https://github.com/pre-commit/mirrors-mypy 26 | rev: v1.16.0 # Use the sha / tag you want to point at 27 | hooks: 28 | - id: mypy 29 | args: ["--install-types", "--non-interactive", "--ignore-missing-imports", "--check-untyped-defs"] 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | ## [Unreleased] 5 | 6 | ## [1.0.0] - 2024-12-26 7 | 8 | ### Docs 9 | - Add more docs 10 | 11 | 12 | ## [0.2.0] - 2024-12-17 13 | 14 | ### Added 15 | - Support chinese display in `wordcloud` 16 | 17 | 18 | ## [0.1.1] - 2024-12-01 19 | 20 | ### Changed 21 | - Change from `SimHei` to `Noto Sans SC` 22 | 23 | 24 | ## [0.1.0] - 2024-11-30 25 | 26 | ### Added 27 | - Support chinese in `matplotlib` 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mathew Shen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zh-Plot: 一行代码搞定 Python 图表中文展示 2 | 3 | [![Python](https://img.shields.io/pypi/pyversions/zhplot.svg?color=%2334D058)](https://pypi.org/project/zhplot/) 4 | [![PyPI](https://img.shields.io/pypi/v/zhplot?color=%2334D058&label=pypi%20package)](https://pypi.org/project/zhplot/) 5 | [![PyPI Downloads](https://static.pepy.tech/badge/zhplot)](https://pepy.tech/projects/zhplot) 6 | 7 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 8 | [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) 9 | [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) 10 | [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) 11 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit) 12 | 13 | [![Build Docs](https://github.com/shenxiangzhuang/zhplot/actions/workflows/build_docs.yaml/badge.svg)](https://github.com/shenxiangzhuang/zhplot/actions/workflows/build_docs.yaml) 14 | [![Test](https://github.com/shenxiangzhuang/zhplot/actions/workflows/test.yaml/badge.svg)](https://github.com/shenxiangzhuang/zhplot/actions/workflows/test.yaml) 15 | [![Codecov](https://codecov.io/gh/shenxiangzhuang/zhplot/branch/master/graph/badge.svg)](https://codecov.io/gh/shenxiangzhuang/zhplot) 16 | [![GitHub License](https://img.shields.io/github/license/shenxiangzhuang/zhplot)](https://github.com/shenxiangzhuang/zhplot/blob/master/LICENSE) 17 | 18 | 19 | ## 支持的框架 20 | - [x] [matplotlib](https://github.com/matplotlib/matplotlib) 21 | - [x] [wordcloud](https://github.com/amueller/word_cloud) 22 | 23 | 24 | ## 快速开始 25 | 26 | ### 安装 27 | 28 | 使用 [pip](https://pip.pypa.io/) 安装`zhplot`: 29 | 30 | ```bash 31 | pip install zhplot 32 | ``` 33 | 34 | 使用 [uv](https://docs.astral.sh/uv/) 安装`zhplot`: 35 | 36 | ```bash 37 | uv add zhplot 38 | ``` 39 | 40 | 41 | ### 使用方法 42 | 43 | 使用`zhplot`非常简单,只需在脚本开头导入即可: 44 | ```diff 45 | + import zhplot 46 | import matplotlib.pyplot as plt 47 | ``` 48 | 49 | 在汉化 wordcloud 的时候`import zhplot`需要在前面: 50 | ```diff 51 | + import zhplot 52 | import wordcloud 53 | ``` 54 | 55 | ### 一个简单的例子 56 |
57 | 58 |
59 | 60 | ```python 61 | import zhplot 62 | import matplotlib.pyplot as plt 63 | 64 | 65 | plt.plot([1, 2, 3, 4]) 66 | plt.title('这是一个标题') 67 | plt.xlabel('横坐标') 68 | plt.ylabel('纵坐标') 69 | plt.show() 70 | ``` 71 | 72 | 73 | ## 相似项目 74 | - [japanize-matplotlib](https://github.com/uehara1414/japanize-matplotlib) 75 | - [chineseize-matplotlib](https://github.com/cndeng/chineseize-matplotlib) 76 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | --8<-- "CHANGELOG.md" 2 | -------------------------------------------------------------------------------- /docs/images/zhplot_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenxiangzhuang/zhplot/f5d6d788fef310d72022037ad7531fa6a84d4473/docs/images/zhplot_demo.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --8<-- "README.md" 2 | -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Zh-Plot 2 | repo_url: https://github.com/shenxiangzhuang/zhplot 3 | repo_name: shenxiangzhuang/zhplot 4 | site_description: Show Chinese in Figures with one line code. 5 | site_author: Xiangzhuang Shen 6 | copyright: Copyright © 2024 Xiangzhuang Shen 7 | 8 | # Page tree 9 | nav: 10 | - Home: index.md 11 | - Changelog: changelog.md 12 | - Author's website: https://shenxiangzhuang.github.io/ 13 | 14 | theme: 15 | name: material 16 | custom_dir: docs/overrides 17 | icon: 18 | repo: fontawesome/brands/github 19 | language: en 20 | palette: 21 | - media: "(prefers-color-scheme: light)" 22 | scheme: default 23 | primary: teal 24 | accent: deep purple 25 | toggle: 26 | icon: material/weather-sunny 27 | name: Switch to dark mode 28 | - media: "(prefers-color-scheme: dark)" 29 | scheme: slate 30 | primary: cyan 31 | accent: deep purple 32 | toggle: 33 | icon: material/weather-night 34 | name: Switch to light mode 35 | features: 36 | - content.action.edit 37 | - content.action.view 38 | - announce.dismiss 39 | - content.code.annotate 40 | - content.tabs.link 41 | - content.tooltips 42 | - header.autohide 43 | - navigation.instant 44 | - navigation.tracking 45 | - navigation.tabs 46 | - navigation.tabs.sticky 47 | - navigation.indexes 48 | - navigation.prune 49 | - navigation.sections 50 | - navigation.top 51 | - search.highlight 52 | - search.share 53 | - search.suggest 54 | - toc.follow 55 | # - toc.integrate 56 | - content.code.annotate 57 | 58 | docs_dir: docs 59 | 60 | 61 | extra_javascript: 62 | - js/config.js 63 | - https://polyfill.io/v3/polyfill.min.js?features=es6 64 | - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js 65 | 66 | extra: 67 | social: 68 | - icon: fontawesome/brands/github 69 | link: https://github.com/shenxiangzhuang/ 70 | - icon: fontawesome/brands/linkedin 71 | link: https://www.linkedin.com/in/xiangzhuang-shen-a81825157/ 72 | 73 | markdown_extensions: 74 | - def_list 75 | - pymdownx.tasklist: 76 | custom_checkbox: true 77 | - toc: 78 | permalink: true 79 | - pymdownx.arithmatex: 80 | generic: true 81 | - pymdownx.highlight: 82 | linenums_style: pymdownx.inline 83 | - pymdownx.superfences 84 | - pymdownx.inlinehilite 85 | - pymdownx.details 86 | - pymdownx.snippets: 87 | url_download: true 88 | - pymdownx.tabbed: 89 | alternate_style: true 90 | - abbr 91 | - admonition 92 | - attr_list 93 | - footnotes 94 | 95 | plugins: 96 | - search 97 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools >= 61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | 6 | [project] 7 | name = "zhplot" 8 | version = "1.0.0" 9 | description = " 一行代码搞定Python图表中文展示(Show Chinese in Figures with one line code)" 10 | authors = [ 11 | {name = "Xiangzhuang Shen", email = "datahonor@gmail.com"}, 12 | ] 13 | license = {text = "MIT"} 14 | readme = "README.md" 15 | 16 | 17 | classifiers=[ 18 | "Development Status :: 5 - Production/Stable", 19 | "Intended Audience :: Science/Research", 20 | "Intended Audience :: Developers", 21 | "License :: OSI Approved :: MIT License", 22 | "Operating System :: OS Independent", 23 | "Framework :: Matplotlib", 24 | "Programming Language :: Python :: 3.9", 25 | "Programming Language :: Python :: 3.10", 26 | "Programming Language :: Python :: 3.11", 27 | "Programming Language :: Python :: 3.12", 28 | "Programming Language :: Python :: 3.13", 29 | "Typing :: Typed", 30 | ] 31 | requires-python = ">= 3.9" 32 | dependencies = [ 33 | "matplotlib>=3.9.2", 34 | ] 35 | 36 | [project.optional-dependencies] 37 | dev = [ 38 | "pre-commit", 39 | "ipython", 40 | "black", 41 | "flake8", 42 | "mypy", 43 | "isort", 44 | "ruff", 45 | "pytest", 46 | "pytest-cov", 47 | "pytest-sugar", 48 | "hypothesis>=6.112.0", 49 | "commitizen", 50 | ] 51 | docs = [ 52 | "mkdocs", 53 | "mkdocs-material", 54 | "mkdocs-material-extensions", 55 | "mkdocstrings", 56 | "mkdocstrings-python", 57 | "mkdocs-autorefs", 58 | "mkdocs-git-committers-plugin-2", 59 | "mkdocs-git-revision-date-localized-plugin", 60 | ] 61 | 62 | 63 | [project.urls] 64 | "Homepage" = "https://shenxiangzhuang.github.io/zhplot" 65 | "Bug Tracker" = "https://shenxiangzhuang.github.io/zhplot/issues" 66 | "Documentation" = "https://shenxiangzhuang.github.io/zhplot" 67 | "Source Code" = "https://github.com/shenxiangzhuang/zhplot" 68 | "Release Notes" = "https://shenxiangzhuang.github.io/zhplot/changelog/" 69 | 70 | [tool.setuptools] 71 | zip-safe = true 72 | include-package-data = true 73 | 74 | [tool.setuptools.packages.find] 75 | include = ["zhplot*"] 76 | namespaces = false 77 | 78 | [tool.setuptools.package-data] 79 | zhplot = ["./fonts/*.ttf", "./fonts/*.txt"] 80 | 81 | 82 | [tool.isort] 83 | profile = "black" 84 | 85 | [tool.ruff] 86 | # Allow lines to be as long as 120. 87 | line-length = 120 88 | 89 | [tool.uv] 90 | extra-index-url = ["https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"] 91 | -------------------------------------------------------------------------------- /tests/test_matplotlib.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | 3 | import zhplot # noqa 4 | 5 | 6 | def test_simple_plot(): 7 | """Test basic plotting with Chinese labels.""" 8 | fig, ax = plt.subplots() 9 | ax.plot([1, 2, 3, 4]) 10 | ax.set_xlabel("简单测试") 11 | plt.close(fig) 12 | 13 | 14 | def test_chinese_font_config(): 15 | """Test if matplotlib is configured to use Chinese fonts.""" 16 | # Check if SimHei is in the font family settings after the import 17 | assert ( 18 | "Noto Sans SC" in plt.rcParams["font.family"] 19 | or any("Noto Sans SC" in name.lower() for name in plt.rcParams["font.sans-serif"]) 20 | ) is True 21 | -------------------------------------------------------------------------------- /tests/test_wordcloud.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import zhplot # noqa 4 | 5 | 6 | def test_chinese_font_config(): 7 | """Test if wordcloud is configured to use Chinese fonts.""" 8 | font_path_env = os.getenv("FONT_PATH") 9 | assert font_path_env is not None 10 | assert "NotoSansSC-Regular" in font_path_env 11 | -------------------------------------------------------------------------------- /zhplot/__init__.py: -------------------------------------------------------------------------------- 1 | from .matplotlib import matplotlib_chineseize 2 | from .wordcloud import wordcloud_chineseize 3 | 4 | __all__ = [ 5 | "matplotlib_chineseize", 6 | "wordcloud_chineseize", 7 | ] 8 | -------------------------------------------------------------------------------- /zhplot/fonts/NotoSansSC-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenxiangzhuang/zhplot/f5d6d788fef310d72022037ad7531fa6a84d4473/zhplot/fonts/NotoSansSC-Regular.ttf -------------------------------------------------------------------------------- /zhplot/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source' 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://openfontlicense.org 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /zhplot/fonts/README.txt: -------------------------------------------------------------------------------- 1 | Noto Sans SC Variable Font 2 | ========================== 3 | 4 | This download contains Noto Sans SC as both a variable font and static fonts. 5 | 6 | Noto Sans SC is a variable font with this axis: 7 | wght 8 | 9 | This means all the styles are contained in a single file: 10 | NotoSansSC-VariableFont_wght.ttf 11 | 12 | If your app fully supports variable fonts, you can now pick intermediate styles 13 | that aren’t available as static fonts. Not all apps support variable fonts, and 14 | in those cases you can use the static font files for Noto Sans SC: 15 | static/NotoSansSC-Thin.ttf 16 | static/NotoSansSC-ExtraLight.ttf 17 | static/NotoSansSC-Light.ttf 18 | static/NotoSansSC-Regular.ttf 19 | static/NotoSansSC-Medium.ttf 20 | static/NotoSansSC-SemiBold.ttf 21 | static/NotoSansSC-Bold.ttf 22 | static/NotoSansSC-ExtraBold.ttf 23 | static/NotoSansSC-Black.ttf 24 | 25 | Get started 26 | ----------- 27 | 28 | 1. Install the font files you want to use 29 | 30 | 2. Use your app's font picker to view the font family and all the 31 | available styles 32 | 33 | Learn more about variable fonts 34 | ------------------------------- 35 | 36 | https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts 37 | https://variablefonts.typenetwork.com 38 | https://medium.com/variable-fonts 39 | 40 | In desktop apps 41 | 42 | https://theblog.adobe.com/can-variable-fonts-illustrator-cc 43 | https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts 44 | 45 | Online 46 | 47 | https://developers.google.com/fonts/docs/getting_started 48 | https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide 49 | https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts 50 | 51 | Installing fonts 52 | 53 | MacOS: https://support.apple.com/en-us/HT201749 54 | Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux 55 | Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows 56 | 57 | Android Apps 58 | 59 | https://developers.google.com/fonts/docs/android 60 | https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts 61 | 62 | License 63 | ------- 64 | Please read the full license text (OFL.txt) to understand the permissions, 65 | restrictions and requirements for usage, redistribution, and modification. 66 | 67 | You can use them in your products & projects – print or digital, 68 | commercial or otherwise. 69 | 70 | This isn't legal advice, please consider consulting a lawyer and see the full 71 | license for all details. 72 | -------------------------------------------------------------------------------- /zhplot/matplotlib.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from pathlib import Path 3 | 4 | import matplotlib 5 | from matplotlib import font_manager 6 | 7 | 8 | @dataclass 9 | class MatplotlibChineseize: 10 | font_name: str = "Noto Sans SC" 11 | font_ttf: str = "NotoSansSC-Regular.ttf" 12 | 13 | def __post_init__(self): 14 | self.font_dir_path = Path(__file__).parent / "fonts" 15 | self.font_ttf_path = self.font_dir_path / self.font_ttf 16 | 17 | def setup(self): 18 | # Ref: https://github.com/uehara1414/japanize-matplotlib 19 | font_files = font_manager.findSystemFonts(fontpaths=[self.font_dir_path]) 20 | for fpath in font_files: 21 | font_manager.fontManager.addfont(fpath) 22 | matplotlib.rc("font", family=self.font_name) 23 | 24 | 25 | def matplotlib_chineseize(): 26 | MatplotlibChineseize().setup() 27 | 28 | 29 | matplotlib_chineseize() 30 | -------------------------------------------------------------------------------- /zhplot/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenxiangzhuang/zhplot/f5d6d788fef310d72022037ad7531fa6a84d4473/zhplot/py.typed -------------------------------------------------------------------------------- /zhplot/wordcloud.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | 5 | def wordcloud_chineseize(): 6 | font_ttf: str = "NotoSansSC-Regular.ttf" 7 | os.environ["FONT_PATH"] = f"{Path(__file__).parent}/fonts/{font_ttf}" 8 | 9 | 10 | wordcloud_chineseize() 11 | --------------------------------------------------------------------------------