跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonNode.jsAI大前端

Pyodide:在浏览器中运行 Python

Pyodide 是基于 WebAssembly 的 Python 运行时,支持在浏览器和 Node.js 中直接运行 Python 代码。它允许用户通过 micropip 安装纯 Python 包及编译扩展,完整支持 NumPy、pandas 等科学计算库,并提供 JavaScript 与 Python 的双向互操作性。 Pyodide 的核心特性,并通过一个包含 Matplotlib 绘图功能的 HTML 示例,演示了如何在无后端的情况下于浏览器端生成数据图表。

神经兮兮发布于 2026/3/26更新于 2026/7/145.5K 浏览
Pyodide:在浏览器中运行 Python

Python 运行在浏览器

梦想成真

在过去的两年里,我们全身心投入到在各种操作系统上开发和部署 Python 应用的工作中,这一切源于其丰富的库资源、简洁直观的语法以及卓越的跨平台兼容性。我们的工作横跨多个领域,涵盖人工智能与数据科学项目、软件维护与测试、网页开发、数据库服务器管理、物联网监控以及安卓应用开发。

长久以来,我们始终认为 Python 能在网页浏览器中像 JavaScript 一样自由运行似乎只是一个遥不可及的梦想——虽然已有若干开源解决方案试图实现这一目标,但它们都存在不少缺陷。

Pyodide 简介

Pyodide 是一个基于 WebAssembly 的 Python 运行时,可以直接在 Web 浏览器和 Node.js 中运行。它支持用户通过 micropip 安装和运行纯 Python 包以及带有编译扩展的包。NumPy、pandas 和 scikit-learn 等关键科学计算库均获得完整支持。该平台具备 JavaScript 与 Python 之间无缝的双向互操作性,包括错误处理和 async/await 支持。在浏览器中运行时,Pyodide 可完全访问 Web API,实现全面的网络集成。

综合例子(调用 numpy 等科学计算库)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Pyodide 中文图表 - 修复字体问题</title>
    <script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js"></script>
    <style>
        *{margin: 0;padding: 0;box-sizing: border-box;}
        body{font-family:'Microsoft YaHei','微软雅黑', sans-serif;background:linear-gradient(135deg, #667eea 0%, #764ba2 100%);min-height: 100vh;display: flex;justify-content: center;align-items: center;padding: 20px;}
        .container{width: 100%;max-width: 1400px;background: white;border-radius: 20px;box-shadow: 0 20px 40px rgba(0,0,0,0.1);overflow: hidden;}
        header{background:linear-gradient(90deg, #2c3e50 0%, #4a6491 100%);color: white;padding: 30px;text-align: center;}
        h1{font-size: 2.8rem;margin-bottom: 10px;font-weight: bold;}
        .subtitle{font-size: 1.3rem;opacity: 0.9;margin-bottom: 20px;}
        .content{padding: 40px;}
        .controls{display: grid;grid-template-columns:repeat(auto-fit,minmax(250px, 1fr));gap: 20px;margin-bottom: 40px;}
        .btn{background:linear-gradient(90deg, #4776E6 0%, #8E54E9 100%);color: white;border: none;padding: 18px 25px;border-radius: 12px;font-size: 1.2rem;cursor: pointer;transition: all 0.3s ease;display: flex;align-items: center;justify-content: center;gap: 10px;font-weight: bold;}
        .btn:hover{transform:translateY(-3px);box-shadow: 0 10px 20px rgba(0,0,0,0.2);}
        .btn:active{transform:translateY(0);}
        .btn:disabled{background: #cccccc;cursor: not-allowed;transform: none;box-shadow: none;opacity: 0.7;}
        #status{padding: 20px;margin: 30px 0;border-radius: 12px;text-align: center;font-size: 1.1rem;font-weight: bold;}
        .loading{background:linear-gradient(90deg, #e3f2fd, #bbdefb);color: #1565c0;border-left: 6px solid #2196f3;}
        .success{background:linear-gradient(90deg, #e8f5e9, #c8e6c9);color: #2e7d32;border-left: 6px solid #4caf50;}
        .error{background:linear-gradient(90deg, #ffebee, #ffcdd2);color: #c62828;border-left: 6px solid #f44336;}
        #plotContainer{min-height: 700px;border: 3px dashed #e0e0e0;border-radius: 15px;background: #f8f9fa;display: flex;justify-content: center;align-items: center;padding: 30px;margin-bottom: 30px;position: relative;}
        #plotContainer img{max-width: 100%;max-height: 650px;box-shadow: 0 10px 30px rgba(0,0,0,0.15);border-radius: 10px;border: 1px solid #ddd;}
        .chart-info{background:linear-gradient(90deg, #f8f9fa, #e9ecef);padding: 25px;border-radius: 12px;margin-top: 30px;border-left: 5px solid #4b6cb7;}
        .chart-info h3{color: #2c3e50;margin-bottom: 15px;font-size: 1.5rem;display: flex;align-items: center;gap: 10px;}
        .chart-info p{margin: 8px 0;line-height: 1.8;font-size: 1.1rem;}
        .info-grid{display: grid;grid-template-columns:repeat(auto-fit,minmax(250px, 1fr));gap: 15px;margin-top: 20px;}
        .info-item{background: white;padding: 15px;border-radius: 8px;border-left: 4px solid #667eea;}
        footer{text-align: center;padding: 25px;color: #666;border-top: 1px solid #eee;background: #f8f9fa;font-size: 1rem;}
        .font-loading{font-size: 0.9rem;color: #666;margin-top: 10px;}
        @media(max-width: 768px){.container{margin: 10px;border-radius: 15px;}header{padding: 20px;}h1{font-size: 2rem;}.content{padding: 20px;}.controls{grid-template-columns: 1fr;}.btn{padding: 15px;font-size: 1.1rem;}#plotContainer{min-height: 500px;padding: 15px;}}
    </style>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
</head>
<body>
    <div class="container">
        <header>
            <h1>🎨 Python + Matplotlib 中文图表</h1>
            <div class="subtitle">在浏览器中运行 Python,完美支持中文显示</div>
        </header>
        <div class="content">
            <div class="controls">
                <button class="btn" onclick="initPyodide()" id="btnInit"><span>🚀</span> 初始化环境 </button>
                <button class="btn" onclick="createChart1()" id="btnChart1" disabled><span>📈</span> 正弦函数图 </button>
                <button class="btn" onclick="createChart2()" id="btnChart2" disabled><span>🎯</span> 散点分布图 </button>
                <button class="btn" onclick="createChart3()" id="btnChart3" disabled><span>📊</span> 销售柱状图 </button>
                <button class="btn" onclick="createChart4()" id="btnChart4" disabled><span>🎨</span> 多图对比 </button>
            </div>
            <div id="status" class="loading">请点击'初始化环境'按钮开始</div>
            <div id="plotContainer">
                <div style="text-align: center;color: #666;">
                    <h3 style="margin-bottom: 15px;">👆 点击上方按钮生成图表</h3>
                    <p>图表将在这里显示</p>
                    <p class="font-loading">正在加载环境...</p>
                </div>
            </div>
            <div id="chartInfo" class="chart-info">
                <h3><span>📋</span> 图表信息</h3>
                <p>等待生成图表...</p>
            </div>
        </div>
        <footer>
            <div style="display: flex;justify-content: space-between;align-items: center;">
                <div>
                    <p style="margin: 0;">© <script>document.write(new Date().getFullYear())</script> Pyodide 学习项目</p>
                    <p style="margin: 5px 0 0 0;font-size: 0.9rem;color: #888;">技术栈:HTML5 + JavaScript + Pyodide + Matplotlib</p>
                </div>
                <div style="text-align: right;">
                    <p style="margin: 0;"><a href="https://pyodide.org" target="_blank" style="color: #666;text-decoration: none;">Pyodide 官网</a> | <a href="https://matplotlib.org" target="_blank" style="color: #666;text-decoration: none;">Matplotlib 文档</a></p>
                    <p style="margin: 5px 0 0 0;font-size: 0.9rem;color: #888;">本页面完全在浏览器端运行</p>
                </div>
            </div>
        </footer>
    </div>
    <script>
        let pyodide = null;
        let isInitialized = false;

        async function initPyodide(){
            try{
                updateStatus('第一步:正在加载 Pyodide 环境... (约 15-25 秒)','loading');
                disableAllButtons(true);
                pyodide = await loadPyodide({indexURL:"https://cdn.jsdelivr.net/pyodide/v0.25.0/full/"});
                updateStatus('第二步:正在安装基础 Python 包...','loading');
                await pyodide.loadPackage(["numpy","matplotlib","scipy"]);
                updateStatus('✅ 环境初始化完成!现在可以生成图表了。','success');
                enableChartButtons();
                isInitialized = true;
                setTimeout(()=>createChart1(),500);
            }catch(error){
                updateStatus(`❌ 初始化失败:${error.message}`,'error');
                console.error("初始化错误:", error);
            }
        }

        async function createChart1(){
            if(!checkInitialized()) return;
            try{
                updateStatus('正在生成正弦函数图表...','loading');
                disableChartButtons(true);
                await pyodide.runPythonAsync(`
                    import matplotlib.pyplot as plt
                    import numpy as np
                    import io
                    import base64
                    from js import document
                    import matplotlib
                    plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial Unicode MS', 'Arial']
                    plt.rcParams['axes.unicode_minus'] = False
                    use_chinese = False
                    x = np.linspace(0, 4 * np.pi, 200)
                    y = np.sin(x)
                    fig, ax = plt.subplots(figsize=(12, 7), dpi=100)
                    ax.plot(x, y, color='#FF6B6B', linewidth=3.5, label='Sine Function: y = sin(x)')
                    ax.fill_between(x, y, alpha=0.15, color='#FF6B6B')
                    if use_chinese:
                        ax.set_title('正弦函数图像演示', fontsize=18, fontweight='bold', pad=20)
                        ax.set_xlabel('角度 (弧度)', fontsize=14, labelpad=10)
                        ax.set_ylabel('函数值 y', fontsize=14, labelpad=10)
                    else:
                        ax.set_title('Sine Function Demonstration', fontsize=18, fontweight='bold', pad=20)
                        ax.set_xlabel('Angle (radians)', fontsize=14, labelpad=10)
                        ax.set_ylabel('Function Value y', fontsize=14, labelpad=10)
                    ax.grid(True, alpha=0.3, linestyle='--', linewidth=0.5)
                    ax.legend(loc='upper right', fontsize=12, frameon=True, shadow=True)
                    special_points = [0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi]
                    special_labels = ['0', 'π/2', 'π', '3π/2', '2π']
                    special_y = np.sin(special_points)
                    ax.scatter(special_points, special_y, color='#4ECDC4', s=150, zorder=5, edgecolors='black', linewidth=2, label='Key Points')
                    for i, (point, y_val, label) in enumerate(zip(special_points, special_y, special_labels)):
                        ax.annotate(f'{label}\\n({y_val:.2f})', xy=(point, y_val), xytext=(10, 30 if i % 2 == 0 else -40), textcoords='offset points', fontsize=11, bbox=dict(boxstyle="round,pad=0.3", facecolor="white", alpha=0.8), arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0.2"))
                    ax.set_xlim(-0.5, 4*np.pi + 0.5)
                    ax.set_ylim(-1.2, 1.2)
                    ax.set_facecolor('#f8f9fa')
                    fig.patch.set_facecolor('white')
                    buf = io.BytesIO()
                    plt.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor=fig.get_facecolor(), edgecolor='none', transparent=False)
                    buf.seek(0)
                    img_str = base64.b64encode(buf.read()).decode('utf-8')
                    plt.close(fig)
                    plot_div = document.getElementById("plotContainer")
                    plot_div.innerHTML = f'''<div><h3>✅ 正弦函数图表生成成功</h3><img src="data:image/png;base64,{img_str}" alt="正弦函数图表"></div>'''
                    info_div = document.getElementById("chartInfo")
                    info_div.innerHTML = f'''<h3><span>📋</span> 正弦函数图表信息</h3><div><div><strong>函数表达式:</strong><br>y = sin(x)</div><div><strong>定义域:</strong><br>x ∈ [0, 4π] ≈ [0, 12.57]</div><div><strong>值域:</strong><br>y ∈ [{y.min():.3f}, {y.max():.3f}]</div><div><strong>周期:</strong><br>2π ≈ 6.283</div><div><strong>数据点数:</strong><br>{len(x)} 个</div><div><strong>图表尺寸:</strong><br>12×7 英寸,150 DPI</div></div>'''
                `);
                updateStatus('✅ 正弦函数图表生成成功!','success');
                disableChartButtons(false);
            }catch(error){
                updateStatus(`❌ 生成图表失败:${error.message}`,'error');
                console.error("图表错误:", error);
                disableChartButtons(false);
            }
        }

        async function createChart2(){
            if(!checkInitialized()) return;
            try{
                updateStatus('正在生成散点分布图...','loading');
                disableChartButtons(true);
                const seed = Math.floor(Math.random()*1000);
                await pyodide.runPythonAsync(`
                    import matplotlib.pyplot as plt
                    import numpy as np
                    import io
                    import base64
                    from js import document
                    plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial']
                    plt.rcParams['axes.unicode_minus'] = False
                    np.random.seed(${seed})
                    n_points = 200
                    x = np.random.randn(n_points) * 2.5
                    y = np.random.randn(n_points) * 2.5
                    colors = np.random.rand(n_points)
                    sizes = 30 + 250 * np.random.rand(n_points)
                    fig, ax = plt.subplots(figsize=(12, 7), dpi=100)
                    ax.set_title('Random Scatter Distribution', fontsize=18, fontweight='bold', pad=20)
                    ax.set_xlabel('X Coordinate', fontsize=14, labelpad=10)
                    ax.set_ylabel('Y Coordinate', fontsize=14, labelpad=10)
                    scatter = ax.scatter(x, y, c=colors, s=sizes, alpha=0.7, cmap='plasma', edgecolors='white', linewidth=0.8)
                    ax.grid(True, alpha=0.3, linestyle='--')
                    cbar = plt.colorbar(scatter)
                    cbar.set_label('Color Intensity', fontsize=12)
                    stats_text = f'''Statistics: • Data Points: {n_points} • X Mean: {x.mean():.3f} • Y Mean: {y.mean():.3f} • Correlation: {np.corrcoef(x, y)[0,1]:.3f}'''
                    ax.text(0.02, 0.98, stats_text, transform=ax.transAxes, fontsize=11, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
                    ax.set_facecolor('#f5f5f5')
                    fig.patch.set_facecolor('white')
                    buf = io.BytesIO()
                    plt.savefig(buf, format='png', dpi=150, bbox_inches='tight')
                    buf.seek(0)
                    img_str = base64.b64encode(buf.read()).decode('utf-8')
                    plt.close(fig)
                    plot_div = document.getElementById("plotContainer")
                    plot_div.innerHTML = f'''<div><h3>✅ 散点图生成成功</h3><img src="data:image/png;base64,{img_str}" alt="散点分布图"></div>'''
                    info_div = document.getElementById("chartInfo")
                    info_div.innerHTML = f'''<h3><span>📋</span> 散点图统计信息</h3><div><div><strong>随机种子:</strong><br>${seed}</div><div><strong>数据规模:</strong><br>{n_points} 个点</div><div><strong>X 统计:</strong><br>均值={x.mean():.3f}, 标准差={x.std():.3f}</div><div><strong>Y 统计:</strong><br>均值={y.mean():.3f}, 标准差={y.std():.3f}</div><div><strong>数据范围:</strong><br>X:[{x.min():.2f}, {x.max():.2f}]<br>Y:[{y.min():.2f}, {y.max():.2f}]</div><div><strong>相关性:</strong><br>{np.corrcoef(x, y)[0,1]:.3f}</div></div>'''
                `);
                updateStatus('✅ 散点图生成成功!','success');
                disableChartButtons(false);
            }catch(error){
                updateStatus(`❌ 生成图表失败:${error.message}`,'error');
                console.error("图表错误:", error);
                disableChartButtons(false);
            }
        }

        async function createChart3(){
            if(!checkInitialized()) return;
            try{
                updateStatus('正在生成销售柱状图...','loading');
                disableChartButtons(true);
                await pyodide.runPythonAsync(`
                    import matplotlib.pyplot as plt
                    import numpy as np
                    import io
                    import base64
                    from js import document
                    plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial']
                    plt.rcParams['axes.unicode_minus'] = False
                    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
                    sales = np.array([85, 67, 92, 78, 88, 95, 110, 105, 98, 120, 115, 130])
                    fig, ax = plt.subplots(figsize=(14, 8), dpi=100)
                    colors = plt.cm.viridis(np.linspace(0.2, 0.8, len(months)))
                    bars = ax.bar(months, sales, color=colors, edgecolor='black', linewidth=1.5, alpha=0.8)
                    for bar in bars:
                        height = bar.get_height()
                        ax.text(bar.get_x() + bar.get_width()/2., height + 1.5, f'{height:.0f}', ha='center', va='bottom', fontsize=11, fontweight='bold')
                    ax.set_title('2024 Annual Sales Data Analysis', fontsize=20, fontweight='bold', pad=25)
                    ax.set_xlabel('Month', fontsize=16, labelpad=15)
                    ax.set_ylabel('Sales (10k yuan)', fontsize=16, labelpad=15)
                    avg_sales = sales.mean()
                    ax.axhline(y=avg_sales, color='red', linestyle='--', linewidth=3, alpha=0.7, label=f'Average: {avg_sales:.1f}')
                    plt.setp(ax.get_xticklabels(), rotation=45, ha='right', rotation_mode='anchor')
                    ax.grid(True, alpha=0.3, axis='y', linestyle='--')
                    ax.set_facecolor('#f8f9fa')
                    fig.patch.set_facecolor('white')
                    ax.set_ylim(0, max(sales) * 1.15)
                    ax.legend(loc='upper left', fontsize=12, frameon=True, shadow=True)
                    total_sales = sales.sum()
                    ax.text(0.02, 0.98, f'Total Sales: {total_sales:.0f}', transform=ax.transAxes, fontsize=14, fontweight='bold', verticalalignment='top', bbox=dict(boxstyle='round', facecolor='gold', alpha=0.8))
                    buf = io.BytesIO()
                    plt.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor=fig.get_facecolor())
                    buf.seek(0)
                    img_str = base64.b64encode(buf.read()).decode('utf-8')
                    plt.close(fig)
                    plot_div = document.getElementById("plotContainer")
                    plot_div.innerHTML = f'''<div><h3>✅ 销售柱状图生成成功</h3><img src="data:image/png;base64,{img_str}" alt="销售柱状图"></div>'''
                    max_month = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'][sales.argmax()]
                    min_month = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'][sales.argmin()]
                    growth = ((sales[-1] - sales[0]) / sales[0]) * 100
                    info_div = document.getElementById("chartInfo")
                    info_div.innerHTML = f'''<h3><span>📋</span> 销售数据分析报告</h3><div><div><strong>年度总销售额:</strong><br>{total_sales:,.0f} 万元</div><div><strong>月平均销售额:</strong><br>{avg_sales:.1f} 万元</div><div><strong>最高销售额月份:</strong><br>{max_month} ({sales.max():.0f}万)</div><div><strong>最低销售额月份:</strong><br>{min_month} ({sales.min():.0f}万)</div><div><strong>年度增长率:</strong><br>{growth:.1f}%</div><div><strong>最佳季度:</strong><br>第四季度</div></div>'''
                `);
                updateStatus('✅ 销售柱状图生成成功!','success');
                disableChartButtons(false);
            }catch(error){
                updateStatus(`❌ 生成图表失败:${error.message}`,'error');
                console.error("图表错误:", error);
                disableChartButtons(false);
            }
        }

        async function createChart4(){
            if(!checkInitialized()) return;
            try{
                updateStatus('正在生成多图对比展示...','loading');
                disableChartButtons(true);
                await pyodide.runPythonAsync(`
                    import matplotlib.pyplot as plt
                    import numpy as np
                    import io
                    import base64
                    from js import document
                    from scipy.stats import norm
                    plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial']
                    plt.rcParams['axes.unicode_minus'] = False
                    fig = plt.figure(figsize=(16, 12), dpi=100)
                    fig.suptitle('Multi-Chart Comparison', fontsize=22, fontweight='bold', y=0.98)
                    ax1 = plt.subplot(2, 2, 1)
                    x = np.linspace(0, 2 * np.pi, 100)
                    ax1.plot(x, np.sin(x), 'b-', linewidth=3, label='Sine Function')
                    ax1.plot(x, np.cos(x), 'r--', linewidth=3, label='Cosine Function')
                    ax1.set_title('Trigonometric Functions', fontsize=16, fontweight='bold')
                    ax1.set_xlabel('Angle (radians)', fontsize=12)
                    ax1.set_ylabel('Function Value', fontsize=12)
                    ax1.grid(True, alpha=0.3)
                    ax1.legend(loc='best')
                    ax1.set_facecolor('#f0f8ff')
                    ax2 = plt.subplot(2, 2, 2)
                    categories = ['Product A', 'Product B', 'Product C', 'Product D', 'Product E']
                    values1 = [85, 92, 78, 88, 95]
                    values2 = [70, 85, 90, 82, 88]
                    x_pos = np.arange(len(categories))
                    width = 0.35
                    ax2.bar(x_pos - width/2, values1, width, label='First Half', color='skyblue', edgecolor='black')
                    ax2.bar(x_pos + width/2, values2, width, label='Second Half', color='lightcoral', edgecolor='black')
                    ax2.set_title('Product Sales Comparison', fontsize=16, fontweight='bold')
                    ax2.set_xlabel('Product Type', fontsize=12)
                    ax2.set_ylabel('Sales (thousand units)', fontsize=12)
                    ax2.set_xticks(x_pos)
                    ax2.set_xticklabels(categories)
                    ax2.legend()
                    ax2.grid(True, alpha=0.3, axis='y')
                    ax2.set_facecolor('#fff5ee')
                    ax3 = plt.subplot(2, 2, 3)
                    labels = ['R&D', 'Marketing', 'Design', 'Service', 'Admin']
                    sizes = [30, 25, 20, 15, 10]
                    colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#c2c2f0']
                    explode = (0.05, 0, 0, 0, 0)
                    wedges, texts, autotexts = ax3.pie(sizes, explode=explode, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True, startangle=90, textprops={'fontsize': 11})
                    ax3.set_title('Department Budget Distribution', fontsize=16, fontweight='bold')
                    for autotext in autotexts:
                        autotext.set_color('white')
                        autotext.set_fontweight('bold')
                    ax3.set_facecolor('#f8f8ff')
                    ax4 = plt.subplot(2, 2, 4)
                    data = np.random.randn(1000)
                    n, bins, patches = ax4.hist(data, bins=30, density=True, alpha=0.7, color='green', edgecolor='black')
                    x_range = np.linspace(-4, 4, 100)
                    pdf = norm.pdf(x_range, loc=data.mean(), scale=data.std())
                    ax4.plot(x_range, pdf, 'r-', linewidth=2, label='Normal Distribution Fit')
                    ax4.set_title('Normal Distribution Histogram', fontsize=16, fontweight='bold')
                    ax4.set_xlabel('Value', fontsize=12)
                    ax4.set_ylabel('Frequency', fontsize=12)
                    ax4.grid(True, alpha=0.3)
                    ax4.legend()
                    ax4.set_facecolor('#f0f0f0')
                    plt.tight_layout(rect=[0, 0.03, 1, 0.95])
                    fig.patch.set_facecolor('white')
                    buf = io.BytesIO()
                    plt.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor=fig.get_facecolor())
                    buf.seek(0)
                    img_str = base64.b64encode(buf.read()).decode('utf-8')
                    plt.close(fig)
                    plot_div = document.getElementById("plotContainer")
                    plot_div.innerHTML = f'''<div><h3>✅ 多图对比展示生成成功</h3><img src="data:image/png;base64,{img_str}" alt="多图对比展示"></div>'''
                    info_div = document.getElementById("chartInfo")
                    info_div.innerHTML = f'''<h3><span>📋</span> 多图表综合信息</h3><div><div><strong>图表总数:</strong><br>4 个不同类型图表</div><div><strong>图表类型:</strong><br>线图、柱状图、饼图、直方图</div><div><strong>数据总量:</strong><br>约 1500+ 个数据点</div><div><strong>图表尺寸:</strong><br>16×12 英寸,150 DPI</div><div><strong>技术特点:</strong><br>使用 scipy 进行统计分析</div><div><strong>生成技术:</strong><br>Pyodide + Matplotlib</div></div>'''
                `);
                updateStatus('✅ 多图对比展示生成成功!','success');
                disableChartButtons(false);
            }catch(error){
                updateStatus(`❌ 生成图表失败:${error.message}`,'error');
                console.error("图表错误:", error);
                disableChartButtons(false);
            }
        }

        function updateStatus(message, type){
            const statusEl = document.getElementById('status');
            statusEl.innerHTML = message;
            statusEl.className = type;
        }

        function disableAllButtons(disabled){
            ['btnInit','btnChart1','btnChart2','btnChart3','btnChart4'].forEach(id=>{
                const btn = document.getElementById(id);
                if(btn){ btn.disabled = disabled;}
            });
        }

        function disableChartButtons(disabled){
            ['btnChart1','btnChart2','btnChart3','btnChart4'].forEach(id=>{
                const btn = document.getElementById(id);
                if(btn){ btn.disabled = disabled;}
            });
        }

        function enableChartButtons(){
            ['btnChart1','btnChart2','btnChart3','btnChart4'].forEach(id=>{
                const btn = document.getElementById(id);
                if(btn){ btn.disabled =false;}
            });
        }

        function checkInitialized(){
            if(!isInitialized){alert('请先点击'初始化环境'按钮!');return false;}
            return true;
        }
    </script>
</body>
</html>

目录

  1. Python 运行在浏览器
  2. 梦想成真
  3. Pyodide 简介
  4. 综合例子(调用 numpy 等科学计算库)
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 写作辅助平台评测:炼字工坊与蛙蛙写作
  • LeetCode Hot 100 链表进阶:反转与排序实战 (Python)
  • Python 3.12 Logging 核心:LogRecord 机制解析与定制实战
  • RocketMQ Java 生态消息中间件架构与实战
  • C++ STL list 模拟实现:从底层链表到容器封装
  • 基于 Function Calling 连接大模型与业务系统
  • Sublime Text 3 在 Windows 10 上的安装与配置指南
  • MidJourney 创作 AI 艺术详细教程
  • 牛客 NC221681 dd 爱框框:滑动窗口解法实战
  • ERNIE-4.5 模型系列解析:架构创新与多场景性能测评
  • 国内直连AI绘画工具实践指南及Stable Diffusion部署方案
  • C++11 新特性深度解析:可变参数模板与 Lambda 表达式
  • HTTPS 真的能防止重放攻击吗?
  • Llama-3.2-3B 部署优化:Ollama 配置上下文窗口与 Token 限制
  • Spring Boot 视图层开发:主流模板引擎集成实战
  • 66 个机器人开源项目合集:科研、教育、工业与医疗资源整理
  • Go 语言字符串转小写算法实现与源码
  • C++ priority_queue 与 deque 的使用及模拟实现
  • 二分算法实战:A-B 数对与高考志愿问题解析
  • 从命令行到自动诊断:构建 AI 驱动的故障树与交互式排障系统

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online