Flutter到HarmonyOS Next 的跨越:memory_info库的鸿蒙适配之旅

Flutter到鸿蒙的跨越:memory_info库的鸿蒙适配之旅

本项目作者:kirk/坚果

您可以使用这个Flutter插件来更改应用程序图标上的角标

作者仓库:https://github.com/MrOlolo/memory_info/tree/master/memory_info

在数字化浪潮的推动下,跨平台开发框架如 Flutter 凭借其高效、便捷的特性,成为了开发者们的宠儿。而鸿蒙系统的崛起,更是为跨平台开发注入了新的活力。为了助力开发者在鸿蒙生态中快速实现 memory_info可帮助您获取设备内存信息(ram&rom),本文将深入浅出地为大家解析如何适配 memory_info 三方库至鸿蒙平台。

一、适配鸿蒙版 memory_info 三方库

(一)版本选择与仓库简介

我们先去 pub 上查看最新版本,我们选择以 0.0.10版本为基础进行适配。memory_info可帮助您获取设备内存信息(ram&rom),其 GitHub 仓库为https://github.com/MrOlolo/memory_info/tree/master/memory_info ,我们的目标是将这个插件适配到鸿蒙平台。

(二)引入背景与使用场景

在 OpenHarmony 北向生态的发展过程中,许多已经适配了 Flutter 的厂商在接入 OpenHarmony 时,都希望能够继续使用 memory_info 来实现内存查看功能。因此,我们提供了这个适配方案,采用插件化的适配器模式,帮助生态伙伴快速实现产品化。

本方案适用于已经支持 Flutter 框架的设备在移植到 OpenHarmony 系统过程中,作为一个备选方案。

(三)使用文档与插件库使用

适配 OpenHarmony 平台的详细使用指导可以参考:Flutter使用指导文档

在项目中使用该插件库时,只需在 pubspec.yaml 文件的 dependencies 中新增如下配置:

dependencies:
  memory_info:
    git:
      url: "https://gitcode.com/nutpi/memory_info.git"
      path: ""

然后在项目根目录运行 flutter pub get,即可完成依赖添加

接下来是具体的适配过程。

二、适配过程详解

(一)准备工作

确保已经配置好了 Flutter 开发环境,具体可参考 Flutter 配置指南。同时,从 官方插件库 下载待适配的三方插件。本指导书, 以适配 memory_info 为例

image-20250417200546042

(二)插件目录结构

下载并解压插件后,我们会看到以下目录结构:

  • lib :对接 Dart 端代码的入口,由此文件接收到参数后,通过 channel 将数据发送到原生端。
  • android :安卓端代码实现目录。
  • ios :iOS 原生端实现目录。
  • example :一个依赖于该插件的 Flutter 应用程序,用于说明如何使用它。
  • README.md :介绍包的文件。
  • CHANGELOG.md :记录每个版本中的更改。
  • LICENSE :包含软件包许可条款的文件。

(三)创建插件的鸿蒙模块

在插件目录下,打开 Terminal,执行以下命令来创建一个鸿蒙平台的 Flutter 模块:

flutter create . --org com.mrololo.memory_info --template=plugin --platforms=ohos

步骤:

  1. 用vscode/trae打开刚刚下载好的插件。

  2. 打开Terminal,cd到插件目录下。

  3. 执行命令flutter create . --org com.mrololo.memory_info --template=plugin --platforms=ohos 创建一个ohos平台的flutter模块。

第一个问题,修改sdk的版本,适配旧版本。

我们做好修改就好。

(四)在根目录下添加鸿蒙平台配置

在项目根目录的 pubspec.yaml 文件中,添加鸿蒙平台的相关配置:

name: memory_info
description: Flutter package to get device memory info(ram&rom) at android/ios devices
repository: https://com.nutpi.memory_info/
version: 0.0.4

environment:
  sdk: ">=2.12.0 <4.0.0"
  flutter: ">=1.20.0"

dependencies:
  flutter:
    sdk: flutter

dev_dependencies:
  flutter_test:
    sdk: flutter# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec# The following section is specific to Flutter.
flutter:
  plugin:
    platforms:
      android:
        package: com.mrololo.memory_info
        pluginClass: MemoryInfoPlugin
      ios:
        pluginClass: MemoryInfoPlugin
      ohos:
        package: com.mrololo.memory_info
        pluginClass: MemoryInfoPlugin

(五)编写鸿蒙插件的原生 ArkTS模块

1. 创建鸿蒙插件模块

使用 DevEco Studio 打开鸿蒙项目。

2. 修改相关配置文件

ohos 目录内的 oh-package.json5 文件中添加 libs/flutter.har 依赖,并创建 .gitignore 文件,添加以下内容以忽略 libs 目录:

/node_modules
/oh_modules
/local.properties
/.preview
/.idea
/build
/libs
*.har
/.cxx
/.test
/BuildProfile.ets
/oh-package-lock.json5

oh-package.json5 文件内容如下:

{"name": "memory_info","version": "1.0.0","description": "Flutter package to get device memory info(ram&rom) at Android/ios/OpenHarmony devices","main": "index.ets","author": "nutpi","license": "Apache-2.0","dependencies": {"@ohos/flutter_ohos": "file:./har/flutter.har"}
}

ohos 目录下创建 index.ets 文件,导出配置:

import MemoryInfoPlugin from './src/main/ets/components/plugin/MemoryInfoPlugin';
export default MemoryInfoPlugin;
3. 编写 ETS 代码

ohos的api可以参考:https://gitcode.com/openharmony/docs

以下是 MemoryInfoPlugin 文件的代码示例:

import {FlutterPlugin,FlutterPluginBinding,MethodCall,MethodCallHandler,MethodChannel,MethodResult,
} from '@ohos/flutter_ohos';
import { storageStatistics,statfs } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hidebug } from '@kit.PerformanceAnalysisKit';
import appManager from '@ohos.application.appManager';/** MemoryInfoPlugin **/
export default class MemoryInfoPlugin implements FlutterPlugin, MethodCallHandler {private channel: MethodChannel | null = null;constructor() {}getUniqueClassName(): string {return "MemoryInfoPlugin"}onAttachedToEngine(binding: FlutterPluginBinding): void {this.channel = new MethodChannel(binding.getBinaryMessenger(), "com.nutpi.memory_info");this.channel.setMethodCallHandler(this)}onDetachedFromEngine(binding: FlutterPluginBinding): void {if (this.channel != null) {this.channel.setMethodCallHandler(null)}}onMethodCall(call: MethodCall, result: MethodResult)  {if (call.method == "getPlatformVersion") {result.success("OpenHarmony ^ ^ ")} else if (call.method == "getDiskSpace") {try {const Info = new Map<string, number>();storageStatistics.getTotalSize()// 获取内置存储的总空间大小(单位为Byte),同步返回let diskTotalSpaceBytes =  storageStatistics.getFreeSizeSync();// 获取内置存储的可用空间大小(单位为Byte),同步返回let diskFreeSpaceBytes =  storageStatistics.getTotalSizeSync();// 转换为 MBconst mbFactor = 1024 * 1024;let diskTotalSpaceMB = diskTotalSpaceBytes / mbFactor;let diskFreeSpaceMB = diskFreeSpaceBytes / mbFactor;Info.set("diskTotalSpace", diskTotalSpaceMB);Info.set("diskFreeSpace", diskFreeSpaceMB);console.info(`getDiskSpace success: total=${diskTotalSpaceMB}MB, free=${diskFreeSpaceMB}MB`);result.success(Info);} catch (err) {console.error("getDiskSpace failed with error:" + JSON.stringify(err));// 在 Flutter 端,通常期望一个 Map 或者 null/error// 这里返回一个空的 Map 或者可以考虑 result.errorresult.error("DISK_SPACE_ERROR", "Failed to get disk space", JSON.stringify(err));}} else if (call.method == "getMemoryInfo") {const Info = new Map<string, number>();// 转换为 MBconst mbFactor = 1024 * 1024;let systemMemInfo: hidebug.SystemMemInfo = hidebug.getSystemMemInfo();// Convert bigint to number before divisionInfo.set("total", Number(systemMemInfo.totalMem)/mbFactor);Info.set("free", Number(systemMemInfo.freeMem)/mbFactor);Info.set("usedByApp", Number(systemMemInfo.availableMem)/mbFactor);console.info(`totalMem: ${systemMemInfo.totalMem}, freeMem: ${systemMemInfo.freeMem}, ` +`availableMem: ${systemMemInfo.availableMem}`);//获取当前应用的存储空间大小。storageStatistics.getCurrentBundleStats((err: BusinessError, bundleStats: storageStatistics.BundleStats) => {if (err) {console.error(`Invoke getCurrentBundleStats failed, code is ${err.code}, message is ${err.message}`);} else {console.info(`Invoke getCurrentBundleStats succeeded, appsize is ${bundleStats.appSize}`);Info.set("appSize", Number(bundleStats.appSize)/mbFactor);}result.success(Info);});} else {result.notImplemented()}}
}

这里我主要参考的是

三、应用及文件系统空间统计

1.storageStatistics.getCurrentBundleStats

getCurrentBundleStats(): Promise

应用异步获取当前应用存储空间大小(单位为Byte),以Promise方式返回。

系统能力:SystemCapability.FileManagement.StorageService.SpatialStatistics

返回值:

类型说明
Promise<Bundlestats>Promise对象,返回指定卷上的应用存储空间大小(单位为Byte)。

错误码:

以下错误码的详细介绍请参见文件管理错误码

错误码ID错误信息
401The input parameter is invalid. Possible causes: Mandatory parameters are left unspecified.
13600001IPC error.
13900042Unknown error.

示例:

import { BusinessError } from '@kit.BasicServicesKit';storageStatistics.getCurrentBundleStats().then((BundleStats: storageStatistics.BundleStats) => {  console.info("getCurrentBundleStats successfully:" + JSON.stringify(BundleStats));}).catch((err: BusinessError) => {  console.error("getCurrentBundleStats failed with error:"+ JSON.stringify(err));});

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-storage-statistics#storagestatisticsgetfreesize15

2.应用及文件系统空间统计

在系统中,可能出现系统空间不够或者cacheDir等目录受系统配额限制等情况,需要应用开发者关注系统剩余空间,同时控制应用自身占用的空间大小。

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-fs-space-statistics

表1 文件系统空间和应用空间统计

模块接口名功能
@ohos.file.storageStatisticsgetCurrentBundleStats获取当前应用的存储空间大小(单位为Byte)。
@ohos.file.storageStatisticsgetFreeSize异步获取内置存储的总空间大小(单位为Byte)。
@ohos.file.storageStatisticsgetTotalSize异步获取内置存储的可用空间大小(单位为Byte)。

表2 应用空间统计

BundleStats属性含义统计路径
appSize应用安装文件大小(单位为Byte)应用安装文件保存在以下目录:/data/storage/el1/bundle
cacheSize应用缓存文件大小(单位为Byte)应用的缓存文件保存在以下目录:/data/storage/el1/base/cache/data/storage/el1/base/haps/entry/cache/data/storage/el2/base/cache/data/storage/el2/base/haps/entry/cache
dataSize应用文件存储大小(除应用安装文件和缓存文件)(单位为Byte)应用文件由本地文件、分布式文件以及数据库文件组成。本地文件保存在以下目录(注意缓存文件目录为以下目录的子目录):/data/storage/el1/base/data/storage/el2/base分布式文件保存在以下目录:/data/storage/el2/distributedfiles数据库文件保存在以下目录:/data/storage/el1/database/data/storage/el2/database

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-hidebug#hidebuggetsystemmeminfo12

3.使用 HiDebug 模块

HiDebug 提供了一系列接口,可以获取系统的内存信息,进而间接获取 RAM size 。具体步骤如下:

  • 导入 HiDebug 模块 :在代码中导入 HiDebug 模块,import { hidebug } from '@ohos.hidebug';

4.SystemMemInfo

描述系统内存信息。

系统能力:SystemCapability.HiviewDFX.HiProfiler.HiDebug

名称类型必填说明
totalMembigint系统总的内存,以KB为单位,计算方式:/proc/meminfo: MemTotal。
freeMembigint系统空闲的内存,以KB为单位,计算方式:/proc/meminfo: MemFree。
availableMembigint系统可用的内存,以KB为单位,计算方式:/proc/meminfo: MemAvailable
  • usedByApp: 表示当前应用程序占用的内存大小(单位:MB)。这是通过 Runtime 类计算得到的应用已分配内存减去其中的空闲内存。
  • total: 表示设备的总内存(RAM)大小(单位:MB)。这是通过 ActivityManager.MemoryInfo 获取的系统总内存。
  • free: 表示设备当前可用的内存大小(单位:MB)。这是通过 ActivityManager.MemoryInfo 获取的系统可用内存,指系统在开始强制关闭后台进程之前可用的内存。
  • lowMemory: 一个布尔值,表示系统当前是否处于低内存状态。如果为 true,则表示系统内存不足。

5.appManager.isRamConstrainedDevice7+

isRamConstrainedDevice(): Promise

查询是否为ram受限设备。使用Promise异步回调。

系统能力:SystemCapability.Ability.AbilityRuntime.Core

返回值:

类型说明
PromisePromise对象。返回true表示是ram受限设备;返回false表示不是ram受限设备。

示例:

import appManager from '@ohos.application.appManager';import { BusinessError } from '@ohos.base';
appManager.isRamConstrainedDevice().then((data) => {    console.log(`The result of isRamConstrainedDevice is: ${JSON.stringify(data)}`);}).catch((error: BusinessError) => {    console.error(`error: ${JSON.stringify(error)}`);});

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-application-appmanager

6.appManager.isRamConstrainedDevice7+

isRamConstrainedDevice(callback: AsyncCallback): void

查询是否为ram受限设备。使用callback异步回调。

系统能力:SystemCapability.Ability.AbilityRuntime.Core

参数:

参数名类型必填说明
callbackAsyncCallback回调函数。返回true表示当前是ram受限设备;返回false表示当前不是ram受限设备。

示例:

import appManager from '@ohos.application.appManager';
appManager.isRamConstrainedDevice((error, data) => {    if (error && error.code !== 0) {        console.error(`isRamConstrainedDevice fail, error: ${JSON.stringify(error)}`);    } else {        console.log(`The result of isRamConstrainedDevice is: ${JSON.stringify(data)}`);    }});

7.使用 HiDebug 模块

HiDebug 提供了一系列接口,可以获取系统的内存信息,进而间接获取 RAM size 。具体步骤如下:

  • 导入 HiDebug 模块 :在代码中导入 HiDebug 模块,import { hidebug } from '@ohos.hidebug';
  • 调用接口获取内存信息 :调用 hidebug.getSystemMemInfo() 接口获取系统内存信息,该方法返回一个对象,其中包含了系统的总内存、可用内存等信息。

https://github.com/SebghatYusuf/system_info_plus

四、编写 Example

1. 创建 Example 应用

在插件根目录下创建一个名为 example 的文件夹,用于存放示例应用。在 example 文件夹中,创建一个鸿蒙平台的 Flutter 应用,用于验证插件功能。

2. 签名与运行

使用 Deveco Studio 打开 example > ohos 目录,单击 File > Project Structure > Project > Signing Configs,勾选 Automatically generate signature,等待自动签名完成。然后运行以下命令:

flutter pub getflutter build hap --debug

如果应用正常启动,说明插件适配成功。如果没有,欢迎大家联系坚果派一起支持。

五、总结

通过以上步骤,我们成功地将 memory_info 三方库适配到了鸿蒙平台。这个过程涉及到了解插件的基本信息、配置开发环境、创建鸿蒙模块、编写原生代码以及测试验证等多个环节。希望这篇博客能够帮助到需要进行 memory_info可帮助您获取设备内存信息(ram&rom) 鸿蒙适配的开发者们,让大家在鸿蒙生态的开发中更加得心应手。

六、参考

  • [如何使用Flutter与OpenHarmony通信 FlutterChannel](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8Flutter%E4%B8%8EOpenHarmony%E9%80%9A%E4%BF%A1 FlutterChannel.md)
  • [开发module](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E6%B7%B7%E5%90%88%E5%BC%80%E5%8F%91 module.md)
  • 开发package
  • 开发plugin
  • [开发FFI plugin](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/%E5%BC%80%E5%8F%91FFI plugin.md)
  • developing-packages
  • 适配仓库地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/81231.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VLAN扩展技术

端口隔离 &#x1f310; 一、原理总结&#xff1a; 端口隔离功能&#xff1a;实现同一VLAN内端口之间的二层隔离。 用户只需将端口加入同一个隔离组&#xff08;Port-isolate group&#xff09;&#xff0c;即可实现这些端口之间不能互通。 实现效果&#xff1a;更安全、更加…

设计模式 - 单例模式 - Tips

为什么双重检查会带来空指针异常问题&#xff1f; if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } …

【Ragflow】22.RagflowPlus(v0.3.0):用户会话管理/文件类型拓展/诸多优化更新

概述 在历经三周的阶段性开发后&#xff0c;RagflowPlus顺利完成既定计划&#xff0c;正式发布v0.3.0版本。 开源地址&#xff1a;https://github.com/zstar1003/ragflow-plus 新功能 1. 用户会话管理 在后台管理系统中&#xff0c;新增用户会话管理菜单。在此菜单中&…

c++重要知识点汇总(不定期更新)

前言 真心希望各位dalao点赞收藏~ 树状数组 作用&#xff1a;高效求出区间前缀和&#xff0c;允许进行修改操作。 举个栗子&#xff1a; 刚开始有8项&#xff0c;分别为1-8。 首先构建二叉树&#xff1a; 1-8/ |/ |/ |/ |/ |1-4 5-8/ | / |/ | / |1-…

Predict Podcast Listening Time-(回归+特征工程+xgb)

Predict Podcast Listening Time 题意&#xff1a; 给你没个播客的信息&#xff0c;让你预测观众的聆听时间。 数据处理&#xff1a; 1.构造新特征收听效率进行分组 2.对数据异常处理 3.对时间情绪等进行数值编码 4.求某特征值求多项式特征 5.生成特征组合 6.交叉验证并enc…

Class类的详细说明

Class类的详细说明 Class 类是Java反射机制的核心&#xff0c;每个Java类或接口在JVM中都有一个对应的 Class 对象&#xff0c;用于表示该类的元数据&#xff08;如类名、方法、字段、构造器等&#xff09;。以下是其核心知识点&#xff1a; 1. 获取Class对象的三种方式 方式…

[逆向工程]C++实现DLL注入:原理、实现与防御全解析(二十五)

[逆向工程]C实现DLL注入&#xff1a;原理、实现与防御全解析&#xff08;二十五&#xff09; 引言 DLL注入&#xff08;DLL Injection&#xff09;是Windows系统下实现进程间通信、功能扩展、监控调试的核心技术之一。本文将从原理分析、代码实现、实战调试到防御方案&#x…

【ROS2实战】在中国地区 Ubuntu 22.04 上安装 ROS 2 Humble 教程

本文介绍如何在中国大陆环境下顺利安装 ROS 2 Humble&#xff0c;包括使用清华镜像源、解决 locale 和 GPG 密钥问题、安装 ROS 软件包以及配置自动环境加载。 &#x1f31f; ROS 2 版本简介 ROS 2 是机器人操作系统的第二代版本&#xff0c;目前主要有两个长期支持&#xff0…

嵌入式学习笔记 - STM32 ADC 模块工作模式总结

ADC 模式总结&#xff1a; 一 单ADC模式&#xff08;是指ADC1,ADC2,ADC3中只有一个ADC被使用&#xff09; ①单通道&#xff1a; 非连续模式&#xff1a;非连续的意思就是单次&#xff0c;一次转换完成后就停止转换&#xff0c;除非再次被软件或者被外部触发启动&#xff1b…

Python训练打卡Day26

函数专题1&#xff1a;函数定义与参数 知识点回顾&#xff1a; 函数的定义变量作用域&#xff1a;局部变量和全局变量函数的参数类型&#xff1a;位置参数、默认参数、不定参数传递参数的手段&#xff1a;关键词参数传递参数的顺序&#xff1a;同时出现三种参数类型时 到目前为…

使用Docker部署Nacos

sudo systemctl start docker sudo systemctl enable docker docker --version 步骤 2: 拉取 Nacos Docker 镜像 拉取 Nacos 镜像&#xff1a; 你可以从 Docker Hub 上拉取官方的 Nacos 镜像&#xff0c;使用以下命令&#xff1a; docker pull nacos/nacos-server 这会从 …

Ubuntu 添加系统调用

实验内容 通过内核编译法添加一个不用传递参数的系统调用&#xff0c;其功能可自定义。 &#xff08;1&#xff09;添加系统调用号&#xff0c;系统会根据这个号找到syscall_table中的相应表项。具体做法是在syscall_64.tbl文件中添加系统调用号和调用函数的对应关系。 &#…

Javascript:WebAPI

获取网页元素 queryselector queryselector是 JavaScript 中用于选择 DOM 元素的重要方法&#xff0c;它允许使用 CSS 选择器语法来查找页面中的元素。 一般queryselector获取的元素都是html中第一个选择器的元素 支持选择器类型&#xff1a;类选择器(.class) &#xff0c…

十二、Hive 函数

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月1日 专栏&#xff1a;Hive教程 在数据处理的广阔天地中&#xff0c;我们常常需要对数据进行转换、计算、清洗或提取特定信息。Hive 提供了强大的内置运算符和丰富的内置函数库&#xff0c;它们就像魔法师手中的魔法棒&…

Linux之Nginx安装及配置原理篇(一)

Nginx安装及配置 前情回顾 首先针对Nginx进程模型&#xff0c;我们回顾一下它的原理机制&#xff0c;我们知道它是通过Master通过fork分发任务节点给予work节点&#xff0c;然后work节点触发了event事件&#xff0c;之后通过一个access_muttex互斥锁&#xff0c;来单线程调用我…

嵌入式培训之数据结构学习(五)栈与队列

一、栈 &#xff08;一&#xff09;栈的基本概念 1、栈的定义&#xff1a; 注&#xff1a;线性表中的栈在堆区&#xff08;因为是malloc来的&#xff09;&#xff1b;系统中的栈区存储局部变量、函数形参、函数返回值地址。 2、栈顶和栈底&#xff1a; 允许插入和删除的一端…

深度学习---知识蒸馏(Knowledge Distillation, KD)

一、知识蒸馏的本质与起源 定义&#xff1a; 知识蒸馏是一种模型压缩与迁移技术&#xff0c;通过将复杂高性能的教师模型&#xff08;Teacher Model&#xff09;所学的“知识”迁移到轻量级的学生模型&#xff08;Student Model&#xff09;&#xff0c;使学生模型在参数量和计…

ARP Detection MAC-Address Static

一、ARP Detection&#xff08;ARP检测&#xff09; ✅ 定义&#xff1a; ARP检测是一种防止ARP欺骗攻击的安全机制。它通过监控或验证网络中的ARP报文&#xff0c;来判断是否存在伪造的ARP信息。 &#x1f50d; 工作原理&#xff1a; 网络设备&#xff08;如交换机&#xf…

基于 Python 的界面程序复现:标准干涉槽型设计计算及仿真

基于 Python 的界面程序复现&#xff1a;标准干涉槽型设计计算及仿真 在工业设计与制造领域&#xff0c;刀具的设计与优化是提高生产效率和产品质量的关键环节之一。本文将介绍如何使用 Python 复现一个用于标准干涉槽型设计计算及仿真的界面程序&#xff0c;旨在帮助工程师和…

Python绘制南丁格尔玫瑰图:从入门到实战

Python绘制南丁格尔玫瑰图&#xff1a;从入门到实战 引言 南丁格尔玫瑰图&#xff08;Nightingale Rose Chart&#xff09;&#xff0c;也被称为极区图&#xff08;Polar Area Chart&#xff09;&#xff0c;是一种独特的数据可视化方式。这种图表由弗洛伦斯南丁格尔&#xff…