在浏览器里运行Python不是梦-Pyodide

在浏览器里运行Python不是梦-Pyodide

文章目录

Python运行在浏览器

梦想成真

We have abandoned ourselves to developing and deploying Python applications across various operating systems for the last two years, drawn by its abundant libraries, straightforward syntax, and cross-platform compatibility. Our work spans diverse areas including AI and data science projects, software maintenance and testing, web development, database server management, IoT monitoring, and Android applications.
We have long thought that Python running as freely in the web browser as JavaScript does seemed just a dream, because although a few open-source solutions exist to achieve this goal, they have several defects.
在过去的两年里,我们全身心投入到在各种操作系统上开发和部署Python应用的工作中,这一切源于其丰富的库资源、简洁直观的语法以及卓越的跨平台兼容性。我们的工作横跨多个领域,涵盖人工智能与数据科学项目、软件维护与测试、网页开发、数据库服务器管理、物联网监控以及安卓应用开发。

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

Pyodide简介

Pyodide is a WebAssembly-based Python runtime that runs directly in web browsers and Node.js. It enables users to install and run both pure Python packages and those with compiled extensions via micropip. Key scientific libraries like NumPy, pandas, and scikit-learn are fully supported. The platform features seamless bidirectional interoperability between JavaScript and Python, including error handling and async/await support. When running in browsers, Pyodide provides complete access to Web APIs for full web integration.
Pyodide是一个基于WebAssembly的Python运行时,可以直接在Web浏览器和Node.js中运行。它支持用户通过micropip安装和运行纯Python包以及带有编译扩展的包。NumPy、pandas和scikit-learn等关键科学计算库均获得完整支持。该平台具备JavaScript与Python之间无缝的双向互操作性,包括错误处理和async/await支持。在浏览器中运行时,Pyodide可完全访问Web API,实现全面的网络集成。

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

<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>Pyodide 中文图表 - 修复字体问题</title><scriptsrc="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><!-- 加载中文字体 --><linkrel="preconnect"href="https://fonts.googleapis.com"><linkrel="preconnect"href="https://fonts.gstatic.com"crossorigin><linkhref="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap"rel="stylesheet"></head><body><divclass="container"><header><h1>🎨 Python + Matplotlib 中文图表</h1><divclass="subtitle">在浏览器中运行 Python,完美支持中文显示</div></header><divclass="content"><divclass="controls"><buttonclass="btn"onclick="initPyodide()"id="btnInit"><span>🚀</span> 初始化环境 </button><buttonclass="btn"onclick="createChart1()"id="btnChart1"disabled><span>📈</span> 正弦函数图 </button><buttonclass="btn"onclick="createChart2()"id="btnChart2"disabled><span>🎯</span> 散点分布图 </button><buttonclass="btn"onclick="createChart3()"id="btnChart3"disabled><span>📊</span> 销售柱状图 </button><buttonclass="btn"onclick="createChart4()"id="btnChart4"disabled><span>🎨</span> 多图对比 </button></div><divid="status"class="loading"> 请点击"初始化环境"按钮开始 </div><divid="plotContainer"><divstyle="text-align: center;color: #666;"><h3style="margin-bottom: 15px;">👆 点击上方按钮生成图表</h3><p>图表将在这里显示</p><pclass="font-loading">正在加载环境...</p></div></div><divid="chartInfo"class="chart-info"><h3><span>📋</span> 图表信息</h3><p>等待生成图表...</p></div></div><footer><divstyle="display: flex;justify-content: space-between;align-items: center;"><div><pstyle="margin: 0;"> © <script>document.write(newDate().getFullYear())</script> Pyodide 学习项目 </p><pstyle="margin: 5px 0 0 0;font-size: 0.9rem;color: #888;"> 技术栈: HTML5 + JavaScript + Pyodide + Matplotlib </p></div><divstyle="text-align: right;"><pstyle="margin: 0;"><ahref="https://pyodide.org"target="_blank"style="color: #666;text-decoration: none;"> Pyodide 官网 </a> | <ahref="https://matplotlib.org"target="_blank"style="color: #666;text-decoration: none;"> Matplotlib 文档 </a></p><pstyle="margin: 5px 0 0 0;font-size: 0.9rem;color: #888;"> 本页面完全在浏览器端运行 </p></div></div></footer></div><script>let pyodide =null;let isInitialized =false;asyncfunctioninitPyodide(){try{updateStatus('第一步:正在加载 Pyodide 环境... (约 15-25 秒)','loading');disableAllButtons(true);// 加载 Pyodide pyodide =awaitloadPyodide({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);}}asyncfunctioncreateChart1(){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 # 方法1:使用简单的字体设置(避免复杂字体问题) plt.rcParams['font.sans-serif'] = ['DejaVu Sans', 'Arial Unicode MS', 'Arial'] plt.rcParams['axes.unicode_minus'] = False # 方法2:如果上述字体不可用,使用纯英文标签 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') # 将图表保存为 base64 图片 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);}}asyncfunctioncreateChart2(){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);}}asyncfunctioncreateChart3(){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}') # 旋转x轴标签 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') # 设置y轴范围 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> ''' # 更新信息(中文信息可以正常显示,因为这是HTML部分) 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);}}asyncfunctioncreateChart4(){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) # 子图1:函数对比 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') # 子图2:柱状图对比 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') # 子图3:饼图 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') # 子图4:直方图(使用 scipy 的正态分布) 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') # 使用 scipy 计算正态分布 PDF 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);}}// 工具函数functionupdateStatus(message, type){const statusEl = document.getElementById('status'); statusEl.innerHTML = message; statusEl.className = type;}functiondisableAllButtons(disabled){['btnInit','btnChart1','btnChart2','btnChart3','btnChart4'].forEach(id=>{const btn = document.getElementById(id);if(btn){ btn.disabled = disabled;}});}functiondisableChartButtons(disabled){['btnChart1','btnChart2','btnChart3','btnChart4'].forEach(id=>{const btn = document.getElementById(id);if(btn){ btn.disabled = disabled;}});}functionenableChartButtons(){['btnChart1','btnChart2','btnChart3','btnChart4'].forEach(id=>{const btn = document.getElementById(id);if(btn){ btn.disabled =false;}});}functioncheckInitialized(){if(!isInitialized){alert('请先点击"初始化环境"按钮!');returnfalse;}returntrue;}</script></body></html>

运行效果如下:

在这里插入图片描述

Read more

3步实现GitHub全界面中文化 GitHub中文插件完全指南

3步实现GitHub全界面中文化 GitHub中文插件完全指南 【免费下载链接】github-chineseGitHub 汉化插件,GitHub 中文化界面。 (GitHub Translation To Chinese) 项目地址: https://gitcode.com/gh_mirrors/gi/github-chinese GitHub作为全球最大的代码托管平台,其英文界面常成为中文开发者的使用障碍。GitHub中文插件(GitHub Translation To Chinese)通过本地化技术,可将GitHub界面元素一键转换为中文,保留原有功能的同时降低使用门槛。本文将系统介绍这款开源工具的安装配置、核心功能及高级应用技巧,帮助开发者快速构建中文开发环境。 解析GitHub中文插件的核心价值 GitHub中文插件采用轻量级用户脚本架构,通过三大核心优势解决英文界面痛点: 无缝集成的本地化体验 插件在不改变GitHub原有功能布局的前提下,将界面文本替换为精准的中文表述。从导航菜单到按钮文本,从提示信息到帮助文档,实现全界面无死角中文化。这种非侵入式设计确保用户

By Ne0inhk
Trae + Git本地仓库管理(离线)小白一站式指南

Trae + Git本地仓库管理(离线)小白一站式指南

环境 Windows环境,安装trae,git bash。 ps:trae的生态和vscode基本一致,在vscode中也可以仿照操作。 1全局初始化 ctrl+R输入cmd呼出控制台,运行 git --version 显示版本,说明系统环境变量正常,可以往下操作,若报错,重装git bash。 进入Trae,新建终端 配置git用户名和邮箱(离线状态邮箱随便写。若是想要在线状态把代码上传github,需要跟你的github账号保持一致)。在终端窗口中依次键入以下命令: git config --global user.name "<输入你的用户名>" git config --global user.email "<输入你的邮箱>" 2建立本地仓库 2.1

By Ne0inhk
Git 分支管理完全指南:从基础到团队协作

Git 分支管理完全指南:从基础到团队协作

🔥个人主页:Cx330🌸 ❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 🌟心向往之行必能至 🎥Cx330🌸的简介: 目录 前言: 一、为什么要分支?——分支的意义 二. Git 分支基础:核心概念与常用命令 2.1 分支与 HEAD 指针解析 2.2 基础指令:查看、创建、切换分支 三. Git 分支进阶:合并、删除和冲突 3.1 合并分支(git merge 分支名) 3.2 删除分支(

By Ne0inhk
MiroFish:多智能体技术的开源AI推演预测引擎

MiroFish:多智能体技术的开源AI推演预测引擎

MiroFish是一款基于多智能体技术的开源AI预测引擎,能够基于现实种子信息构建平行数字世界进行仿真推演。下面为您详细介绍这个项目以及本地部署和使用流程。 一、MiroFish项目概述 核心功能 1. 种子信息驱动预测:支持从突发新闻、政策草案、金融信号、数据分析报告或小说故事中提取种子信息,生成预测任务输入。 2. 平行数字世界构建:自动搭建高保真仿真环境,让具备独立人格、长期记忆与行为逻辑的智能体在其中自由交互和演化。 3. 自然语言预测交互:用户可直接用自然语言描述预测需求,无需手工编排复杂规则。 4. 预测报告生成:模拟完成后输出详尽预测报告,并由ReportAgent与仿真环境进行深度交互。 5. 模拟世界深度对话:支持与模拟世界中任意角色对话,也可以与报告代理继续追问。 技术架构 * GraphRAG + 长期记忆:种子材料自动拆解成实体关系、人设画像、事件链,Zep Cloud驱动记忆 * OASIS仿真引擎:基于CAMEL-AI团队开源的OASIS引擎,支持数千Agent并行运行 * ReACT模式驱动:ReportAgent采用Reaso

By Ne0inhk