PythonIDE Docs
中文
简体中文

图表与画布实验室

Chart、Canvas、DrawingContext 和阈值可视化。

演示 ChartCanvas/DrawingContext:统计图优先 Chart,自定义示意图再用 Canvas。

#预期效果

运行后会出现图表与画布实验室,图表类型、强调色和阈值会联动更新。

#完整示例

python
import appui

state = appui.State(
    chart_type="bar",
    threshold=55.0,
    accent="systemBlue",
    rows=[
        {"month": "Jan", "value": 42},
        {"month": "Feb", "value": 64},
        {"month": "Mar", "value": 53},
        {"month": "Apr", "value": 78},
        {"month": "May", "value": 69},
    ],
)


def set_chart_type(value):
    state.chart_type = value


def set_threshold(value):
    state.threshold = float(value)


def set_accent(value):
    state.accent = value


def drawing_context():
    ctx = appui.DrawingContext()
    ctx.rounded_rect(
        10, 10, 260, 140,
        corner_radius=14,
        color="secondarySystemBackground",
        fill=True,
    )
    ctx.line(24, 118, 250, 118, color="separator", line_width=1)
    ctx.line(24, 28, 24, 118, color="separator", line_width=1)
    max_value = max(row["value"] for row in state.rows)
    for index, row in enumerate(state.rows):
        x = 42 + index * 42
        height = 80 * row["value"] / max_value
        color = "systemRed" if row["value"] >= state.threshold else state.accent
        ctx.rounded_rect(x, 118 - height, 24, height, corner_radius=5, color=color, fill=True)
        ctx.fill_text(row["month"], x - 3, 132, color="secondaryLabel", font_size=10)
    y = 118 - 80 * state.threshold / max_value
    ctx.line(24, y, 250, y, color="systemOrange", line_width=2)
    ctx.fill_text("阈值", 212, y - 6, color="systemOrange", font_size=11)
    return ctx


def body():
    return appui.NavigationStack(
        appui.ScrollView(
            appui.VStack(
                [
                    appui.Form(
                        [
                            appui.Section(
                                [
                                    appui.Picker(
                                        "图表类型",
                                        selection=state.chart_type,
                                        options=["bar", "line", "area", "point"],
                                        on_change=set_chart_type,
                                    ),
                                    appui.ColorPicker(
                                        "强调色",
                                        selection=state.accent,
                                        on_change=set_accent,
                                    ),
                                    appui.Slider(
                                        value=state.threshold,
                                        minimum=30,
                                        maximum=90,
                                        step=1,
                                        label="阈值",
                                        on_change=set_threshold,
                                    ),
                                    appui.LabeledContent("当前阈值", value=f"{state.threshold:.0f}"),
                                ],
                                header="配置",
                            )
                        ]
                    ),
                    appui.VStack(
                        [
                            appui.Text("系统图表").font("headline"),
                            appui.Chart(
                                state.rows,
                                x="month",
                                y="value",
                                type=state.chart_type,
                                color=state.accent,
                            ).frame(height=220),
                        ],
                        spacing=8,
                    ).padding(horizontal=16),
                    appui.VStack(
                        [
                            appui.Text("Canvas 自定义阈值图").font("headline"),
                            appui.Canvas(width=280, height=160, context=drawing_context()),
                        ],
                        spacing=8,
                    ).padding(horizontal=16),
                ],
                spacing=14,
            )
        ).navigation_title("可视化")
    )


appui.run(body, state=state, presentation="sheet")

#关键技巧

  • Chart 数据为字典列表,x/y 与字段名一致。
  • DrawingContext 在渲染函数里按需构造,不要整段上下文存进 State;命令需可 JSON 序列化。
  • 常规统计用 Canvas 手绘可维护性差,优先 Chart