Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import plotly.graph_objects as go | |
| import plotly.express as px | |
| import json | |
| import os | |
| from datetime import datetime | |
| # 页面配置(必须在最开始) | |
| st.set_page_config( | |
| page_title="溶剂分数转换器", | |
| page_icon="🧪", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # 访问统计功能 | |
| def manage_visit_stats(): | |
| """管理访问统计""" | |
| stats_file = "visit_stats.json" | |
| # 初始化统计数据 | |
| if os.path.exists(stats_file): | |
| try: | |
| with open(stats_file, 'r', encoding='utf-8') as f: | |
| stats = json.load(f) | |
| except: | |
| stats = { | |
| "total_visits": 0, | |
| "daily_visits": {}, | |
| "first_visit": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "last_visit": None | |
| } | |
| else: | |
| stats = { | |
| "total_visits": 0, | |
| "daily_visits": {}, | |
| "first_visit": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "last_visit": None | |
| } | |
| # 检查是否需要更新统计 | |
| if 'visit_counted' not in st.session_state: | |
| st.session_state.visit_counted = True | |
| # 更新统计 | |
| today = datetime.now().strftime("%Y-%m-%d") | |
| stats["total_visits"] += 1 | |
| stats["daily_visits"][today] = stats["daily_visits"].get(today, 0) + 1 | |
| stats["last_visit"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| # 保存统计 | |
| try: | |
| with open(stats_file, 'w', encoding='utf-8') as f: | |
| json.dump(stats, f, ensure_ascii=False, indent=2) | |
| except: | |
| pass # 忽略写入错误 | |
| return stats | |
| # 在页面配置后调用 | |
| visit_stats = manage_visit_stats() | |
| # 自定义CSS样式 | |
| st.markdown(""" | |
| <style> | |
| /* 主标题样式 */ | |
| .main-title { | |
| font-size: 2.5rem; | |
| font-weight: bold; | |
| text-align: center; | |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| margin-bottom: 2rem; | |
| } | |
| /* 卡片样式 */ | |
| .metric-card { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| padding: 1rem; | |
| border-radius: 10px; | |
| color: white; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| margin-bottom: 1rem; | |
| } | |
| /* 侧边栏样式 */ | |
| .sidebar .sidebar-content { | |
| background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); | |
| } | |
| /* 公式框样式 */ | |
| .formula-box { | |
| background: #f8f9fa; | |
| border: 2px solid #e9ecef; | |
| border-radius: 10px; | |
| padding: 1.5rem; | |
| margin: 1rem 0; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| /* 信息框样式 */ | |
| .info-box { | |
| background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin: 1rem 0; | |
| } | |
| /* 警告框样式 */ | |
| .warning-box { | |
| background: linear-gradient(135deg, #fd79a8 0%, #e84393 100%); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin: 1rem 0; | |
| } | |
| /* 数据表格样式 */ | |
| .dataframe { | |
| border-radius: 10px; | |
| overflow: hidden; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| /* 访问统计样式 */ | |
| .visit-stats { | |
| background: linear-gradient(135deg, #00b894 0%, #00a085 100%); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin: 1rem 0; | |
| text-align: center; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # 语言配置 | |
| LANGUAGES = { | |
| "中文": { | |
| "page_title": "溶剂分数转换器", | |
| "main_title": "🧪 溶剂摩尔分数、体积分数与质量分数转换器", | |
| "conversion_mode": "🔄 转换模式", | |
| "mode_mole_to_vol": "摩尔分数 → 体积分数", | |
| "mode_vol_to_mole": "体积分数 → 摩尔分数", | |
| "mode_mole_to_mass": "摩尔分数 → 质量分数", | |
| "mode_mass_to_mole": "质量分数 → 摩尔分数", | |
| "mode_vol_to_mass": "体积分数 → 质量分数", | |
| "mode_mass_to_vol": "质量分数 → 体积分数", | |
| "select_conversion": "选择转换方向:", | |
| "solvent_selection": "🧫 溶剂选择", | |
| "select_solvents": "选择两种溶剂或自定义参数:", | |
| "solvent_a": "溶剂A:", | |
| "solvent_b": "溶剂B:", | |
| "custom": "🛠️ 自定义", | |
| "molar_mass": "摩尔质量", | |
| "density": "密度", | |
| "input_mole_fraction": "📥 输入摩尔分数", | |
| "input_volume_fraction": "📥 输入体积分数", | |
| "input_mass_fraction": "📥 输入质量分数", | |
| "mole_fraction_of": "摩尔分数", | |
| "volume_fraction_of": "体积分数", | |
| "mass_fraction_of": "质量分数", | |
| "result_volume_fraction": "📊 计算结果 - 体积分数", | |
| "result_mole_fraction": "📊 计算结果 - 摩尔分数", | |
| "result_mass_fraction": "📊 计算结果 - 质量分数", | |
| "verification": "✅ 验证", | |
| "detailed_info": "📋 详细信息", | |
| "solvent_params": "🔬 溶剂参数", | |
| "solvent": "溶剂", | |
| "mole_fraction": "摩尔分数", | |
| "volume_fraction": "体积分数", | |
| "mass_fraction": "质量分数", | |
| "mole_verification": "摩尔分数验证", | |
| "volume_verification": "体积分数验证", | |
| "mass_verification": "质量分数验证", | |
| "fraction_visualization": "溶剂分数可视化", | |
| "mole_fraction_chart": "摩尔分数", | |
| "volume_fraction_chart": "体积分数", | |
| "mass_fraction_chart": "质量分数", | |
| "calculation_formula": "📐 计算公式", | |
| "formula_mole_to_vol_title": "**摩尔分数转换为体积分数:**", | |
| "formula_vol_to_mole_title": "**体积分数转换为摩尔分数:**", | |
| "formula_mole_to_mass_title": "**摩尔分数转换为质量分数:**", | |
| "formula_mass_to_mole_title": "**质量分数转换为摩尔分数:**", | |
| "formula_vol_to_mass_title": "**体积分数转换为质量分数:**", | |
| "formula_mass_to_vol_title": "**质量分数转换为体积分数:**", | |
| "formula_where": "其中:", | |
| "usage_instructions": "📖 使用说明", | |
| "instruction_1": "1. **选择转换模式**: 在侧边栏选择转换方向", | |
| "instruction_2": "2. **选择溶剂**: 从预设的常见溶剂中选择,或选择\"自定义\"输入参数", | |
| "instruction_3": "3. **输入数值**: 输入摩尔分数、体积分数或质量分数", | |
| "instruction_4": "4. **查看结果**: 右侧显示转换结果和详细信息", | |
| "note": "⚠️ **注意**: 本计算假设理想混合,实际情况可能存在偏差。", | |
| "language": "🌐 语言", | |
| "visualization": "📈 可视化", | |
| "visit_stats": "📊 访问统计", | |
| "total_visits": "总访问量", | |
| "today_visits": "今日访问", | |
| "last_visit": "上次访问", | |
| "visit_trend": "访问趋势", | |
| "recent_days": "最近访问趋势", | |
| "date": "日期", | |
| "visits": "访问次数" | |
| }, | |
| "English": { | |
| "page_title": "Solvent Fraction Converter", | |
| "main_title": "🧪 Solvent Mole Fraction, Volume Fraction & Mass Fraction Converter", | |
| "conversion_mode": "🔄 Conversion Mode", | |
| "mode_mole_to_vol": "Mole Fraction → Volume Fraction", | |
| "mode_vol_to_mole": "Volume Fraction → Mole Fraction", | |
| "mode_mole_to_mass": "Mole Fraction → Mass Fraction", | |
| "mode_mass_to_mole": "Mass Fraction → Mole Fraction", | |
| "mode_vol_to_mass": "Volume Fraction → Mass Fraction", | |
| "mode_mass_to_vol": "Mass Fraction → Volume Fraction", | |
| "select_conversion": "Select conversion direction:", | |
| "solvent_selection": "🧫 Solvent Selection", | |
| "select_solvents": "Select two solvents or customize parameters:", | |
| "solvent_a": "Solvent A:", | |
| "solvent_b": "Solvent B:", | |
| "custom": "🛠️ Custom", | |
| "molar_mass": "Molar Mass", | |
| "density": "Density", | |
| "input_mole_fraction": "📥 Input Mole Fraction", | |
| "input_volume_fraction": "📥 Input Volume Fraction", | |
| "input_mass_fraction": "📥 Input Mass Fraction", | |
| "mole_fraction_of": "Mole fraction of", | |
| "volume_fraction_of": "Volume fraction of", | |
| "mass_fraction_of": "Mass fraction of", | |
| "result_volume_fraction": "📊 Results - Volume Fraction", | |
| "result_mole_fraction": "📊 Results - Mole Fraction", | |
| "result_mass_fraction": "📊 Results - Mass Fraction", | |
| "verification": "✅ Verification", | |
| "detailed_info": "📋 Detailed Information", | |
| "solvent_params": "🔬 Solvent Parameters", | |
| "solvent": "Solvent", | |
| "mole_fraction": "Mole Fraction", | |
| "volume_fraction": "Volume Fraction", | |
| "mass_fraction": "Mass Fraction", | |
| "mole_verification": "Mole Fraction Verification", | |
| "volume_verification": "Volume Fraction Verification", | |
| "mass_verification": "Mass Fraction Verification", | |
| "fraction_visualization": "Solvent Fraction Visualization", | |
| "mole_fraction_chart": "Mole Fraction", | |
| "volume_fraction_chart": "Volume Fraction", | |
| "mass_fraction_chart": "Mass Fraction", | |
| "calculation_formula": "📐 Calculation Formula", | |
| "formula_mole_to_vol_title": "**Mole Fraction to Volume Fraction:**", | |
| "formula_vol_to_mole_title": "**Volume Fraction to Mole Fraction:**", | |
| "formula_mole_to_mass_title": "**Mole Fraction to Mass Fraction:**", | |
| "formula_mass_to_mole_title": "**Mass Fraction to Mole Fraction:**", | |
| "formula_vol_to_mass_title": "**Volume Fraction to Mass Fraction:**", | |
| "formula_mass_to_vol_title": "**Mass Fraction to Volume Fraction:**", | |
| "formula_where": "Where:", | |
| "usage_instructions": "📖 Usage Instructions", | |
| "instruction_1": "1. **Select Conversion Mode**: Choose conversion direction in sidebar", | |
| "instruction_2": "2. **Select Solvents**: Choose from preset common solvents or select 'Custom' to input parameters", | |
| "instruction_3": "3. **Input Values**: Input mole fraction, volume fraction or mass fraction", | |
| "instruction_4": "4. **View Results**: Results and detailed information are displayed on the right", | |
| "note": "⚠️ **Note**: This calculation assumes ideal mixing; actual situations may deviate.", | |
| "language": "🌐 Language", | |
| "visualization": "📈 Visualization", | |
| "visit_stats": "📊 Visit Statistics", | |
| "total_visits": "Total Visits", | |
| "today_visits": "Today's Visits", | |
| "last_visit": "Last Visit", | |
| "visit_trend": "Visit Trend", | |
| "recent_days": "Recent Visit Trend", | |
| "date": "Date", | |
| "visits": "Visits" | |
| } | |
| } | |
| # 常见溶剂的摩尔质量和密度数据库 | |
| SOLVENTS_DB = { | |
| "中文": { | |
| "水": {"M": 18.02, "density": 1.000, "en": "Water", "color": "#3498db"}, | |
| "乙醇": {"M": 46.07, "density": 0.789, "en": "Ethanol", "color": "#e74c3c"}, | |
| "甲醇": {"M": 32.04, "density": 0.792, "en": "Methanol", "color": "#9b59b6"}, | |
| "异丙醇": {"M": 60.10, "density": 0.786, "en": "Isopropanol", "color": "#f39c12"}, | |
| "丙酮": {"M": 58.08, "density": 0.784, "en": "Acetone", "color": "#2ecc71"}, | |
| "乙酸乙酯": {"M": 88.11, "density": 0.902, "en": "Ethyl Acetate", "color": "#1abc9c"}, | |
| "甲苯": {"M": 92.14, "density": 0.867, "en": "Toluene", "color": "#34495e"}, | |
| "二氯甲烷": {"M": 84.93, "density": 1.326, "en": "Dichloromethane", "color": "#e67e22"}, | |
| "氯仿": {"M": 119.38, "density": 1.489, "en": "Chloroform", "color": "#95a5a6"}, | |
| "四氢呋喃": {"M": 72.11, "density": 0.889, "en": "Tetrahydrofuran", "color": "#8e44ad"}, | |
| "二甲基亚砜": {"M": 78.13, "density": 1.100, "en": "Dimethyl Sulfoxide", "color": "#27ae60"}, | |
| "N,N-二甲基甲酰胺": {"M": 73.09, "density": 0.944, "en": "N,N-Dimethylformamide", "color": "#16a085"}, | |
| "乙腈": {"M": 41.05, "density": 0.786, "en": "Acetonitrile", "color": "#2980b9"}, | |
| "正己烷": {"M": 86.18, "density": 0.659, "en": "n-Hexane", "color": "#d35400"}, | |
| "环己烷": {"M": 84.16, "density": 0.779, "en": "Cyclohexane", "color": "#7f8c8d"}, | |
| "苯": {"M": 78.11, "density": 0.876, "en": "Benzene", "color": "#c0392b"}, | |
| "乙二醇": {"M": 62.07, "density": 1.113, "en": "Ethylene Glycol", "color": "#8e44ad"}, | |
| "甘油": {"M": 92.09, "density": 1.261, "en": "Glycerol", "color": "#f1c40f"} | |
| }, | |
| "English": { | |
| "Water": {"M": 18.02, "density": 1.000, "cn": "水", "color": "#3498db"}, | |
| "Ethanol": {"M": 46.07, "density": 0.789, "cn": "乙醇", "color": "#e74c3c"}, | |
| "Methanol": {"M": 32.04, "density": 0.792, "cn": "甲醇", "color": "#9b59b6"}, | |
| "Isopropanol": {"M": 60.10, "density": 0.786, "cn": "异丙醇", "color": "#f39c12"}, | |
| "Acetone": {"M": 58.08, "density": 0.784, "cn": "丙酮", "color": "#2ecc71"}, | |
| "Ethyl Acetate": {"M": 88.11, "density": 0.902, "cn": "乙酸乙酯", "color": "#1abc9c"}, | |
| "Toluene": {"M": 92.14, "density": 0.867, "cn": "甲苯", "color": "#34495e"}, | |
| "Dichloromethane": {"M": 84.93, "density": 1.326, "cn": "二氯甲烷", "color": "#e67e22"}, | |
| "Chloroform": {"M": 119.38, "density": 1.489, "cn": "氯仿", "color": "#95a5a6"}, | |
| "Tetrahydrofuran": {"M": 72.11, "density": 0.889, "cn": "四氢呋喃", "color": "#8e44ad"}, | |
| "Dimethyl Sulfoxide": {"M": 78.13, "density": 1.100, "cn": "二甲基亚砜", "color": "#27ae60"}, | |
| "N,N-Dimethylformamide": {"M": 73.09, "density": 0.944, "cn": "N,N-二甲基甲酰胺", "color": "#16a085"}, | |
| "Acetonitrile": {"M": 41.05, "density": 0.786, "cn": "乙腈", "color": "#2980b9"}, | |
| "n-Hexane": {"M": 86.18, "density": 0.659, "cn": "正己烷", "color": "#d35400"}, | |
| "Cyclohexane": {"M": 84.16, "density": 0.779, "cn": "环己烷", "color": "#7f8c8d"}, | |
| "Benzene": {"M": 78.11, "density": 0.876, "cn": "苯", "color": "#c0392b"}, | |
| "Ethylene Glycol": {"M": 62.07, "density": 1.113, "cn": "乙二醇", "color": "#8e44ad"}, | |
| "Glycerol": {"M": 92.09, "density": 1.261, "cn": "甘油", "color": "#f1c40f"} | |
| } | |
| } | |
| # 语言选择 | |
| language = st.sidebar.selectbox( | |
| "🌐 Language/语言:", | |
| ["English", "中文"], | |
| help="Choose your preferred language / 选择您的首选语言" | |
| ) | |
| lang = LANGUAGES[language] | |
| # 主标题 | |
| st.markdown(f'<h1 class="main-title">{lang["main_title"]}</h1>', unsafe_allow_html=True) | |
| # 添加分隔线 | |
| st.markdown(""" | |
| <div style="height: 2px; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); margin: 2rem 0;"></div> | |
| """, unsafe_allow_html=True) | |
| # 侧边栏配置 | |
| with st.sidebar: | |
| st.markdown("### " + lang["conversion_mode"]) | |
| conversion_mode = st.radio( | |
| "", | |
| [lang["mode_mole_to_vol"], lang["mode_vol_to_mole"], | |
| lang["mode_mole_to_mass"], lang["mode_mass_to_mole"], | |
| lang["mode_vol_to_mass"], lang["mode_mass_to_vol"]], | |
| help="选择您需要的转换方向" | |
| ) | |
| st.markdown("---") | |
| st.markdown("### " + lang["solvent_selection"]) | |
| # 获取当前语言的溶剂列表 | |
| current_solvents = SOLVENTS_DB[language] | |
| solvent_names = list(current_solvents.keys()) + [lang["custom"]] | |
| # 溶剂A选择 | |
| solvent_a_name = st.selectbox( | |
| lang["solvent_a"], | |
| solvent_names, | |
| help="选择第一种溶剂" | |
| ) | |
| if solvent_a_name == lang["custom"]: | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| M_a = st.number_input(f"{lang['molar_mass']} A (g/mol):", value=18.02, min_value=0.1, step=0.01) | |
| with col2: | |
| rho_a = st.number_input(f"{lang['density']} A (g/cm³):", value=1.000, min_value=0.1, step=0.001) | |
| else: | |
| M_a = current_solvents[solvent_a_name]["M"] | |
| rho_a = current_solvents[solvent_a_name]["density"] | |
| st.info(f"📊 {lang['molar_mass']}: **{M_a}** g/mol \n📊 {lang['density']}: **{rho_a}** g/cm³") | |
| # 溶剂B选择 | |
| solvent_b_name = st.selectbox( | |
| lang["solvent_b"], | |
| solvent_names, | |
| help="选择第二种溶剂" | |
| ) | |
| if solvent_b_name == lang["custom"]: | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| M_b = st.number_input(f"{lang['molar_mass']} B (g/mol):", value=46.07, min_value=0.1, step=0.01) | |
| with col2: | |
| rho_b = st.number_input(f"{lang['density']} B (g/cm³):", value=0.789, min_value=0.1, step=0.001) | |
| else: | |
| M_b = current_solvents[solvent_b_name]["M"] | |
| rho_b = current_solvents[solvent_b_name]["density"] | |
| st.info(f"📊 {lang['molar_mass']}: **{M_b}** g/mol \n📊 {lang['density']}: **{rho_b}** g/cm³") | |
| # 访问统计显示 | |
| st.markdown("---") | |
| st.markdown("### " + lang["visit_stats"]) | |
| # 显示统计信息 | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.metric(lang["total_visits"], visit_stats["total_visits"]) | |
| with col2: | |
| today = datetime.now().strftime("%Y-%m-%d") | |
| today_visits = visit_stats["daily_visits"].get(today, 0) | |
| st.metric(lang["today_visits"], today_visits) | |
| # 显示最近访问时间 | |
| if visit_stats["last_visit"]: | |
| st.caption(f"{lang['last_visit']}: {visit_stats['last_visit']}") | |
| # 显示访问趋势(简单的条形图) | |
| if len(visit_stats["daily_visits"]) > 1: | |
| recent_days = list(visit_stats["daily_visits"].items())[-7:] # 最近7天 | |
| dates = [item[0] for item in recent_days] | |
| counts = [item[1] for item in recent_days] | |
| if len(dates) > 1: | |
| df_visits = pd.DataFrame({ | |
| lang["date"]: dates, | |
| lang["visits"]: counts | |
| }) | |
| fig_visits = px.bar( | |
| df_visits, | |
| x=lang["date"], | |
| y=lang["visits"], | |
| title=lang["recent_days"], | |
| height=200, | |
| color=lang["visits"], | |
| color_continuous_scale="viridis" | |
| ) | |
| fig_visits.update_layout( | |
| showlegend=False, | |
| margin=dict(l=20, r=20, t=40, b=20), | |
| xaxis_title="", | |
| yaxis_title="", | |
| font=dict(size=10) | |
| ) | |
| st.plotly_chart(fig_visits, use_container_width=True) | |
| # 主界面内容 | |
| col1, col2 = st.columns([1, 1], gap="large") | |
| # 定义计算函数 | |
| def calculate_conversion(mode, input_val, M_a, M_b, rho_a, rho_b): | |
| """计算转换结果""" | |
| if mode == lang["mode_mole_to_vol"]: | |
| x_a, x_b = input_val, 1.0 - input_val | |
| V_ratio_a = x_a * M_a / rho_a | |
| V_ratio_b = x_b * M_b / rho_b | |
| V_total = V_ratio_a + V_ratio_b | |
| if V_total > 0: | |
| phi_a = V_ratio_a / V_total | |
| phi_b = V_ratio_b / V_total | |
| else: | |
| phi_a = phi_b = 0 | |
| return (x_a, x_b), (phi_a, phi_b), None | |
| elif mode == lang["mode_vol_to_mole"]: | |
| phi_a, phi_b = input_val, 1.0 - input_val | |
| n_ratio_a = phi_a * rho_a / M_a | |
| n_ratio_b = phi_b * rho_b / M_b | |
| n_total = n_ratio_a + n_ratio_b | |
| if n_total > 0: | |
| x_a = n_ratio_a / n_total | |
| x_b = n_ratio_b / n_total | |
| else: | |
| x_a = x_b = 0 | |
| return (x_a, x_b), (phi_a, phi_b), None | |
| elif mode == lang["mode_mole_to_mass"]: | |
| x_a, x_b = input_val, 1.0 - input_val | |
| mass_a = x_a * M_a | |
| mass_b = x_b * M_b | |
| mass_total = mass_a + mass_b | |
| if mass_total > 0: | |
| w_a = mass_a / mass_total | |
| w_b = mass_b / mass_total | |
| else: | |
| w_a = w_b = 0 | |
| return (x_a, x_b), None, (w_a, w_b) | |
| elif mode == lang["mode_mass_to_mole"]: | |
| w_a, w_b = input_val, 1.0 - input_val | |
| n_ratio_a = w_a / M_a | |
| n_ratio_b = w_b / M_b | |
| n_total = n_ratio_a + n_ratio_b | |
| if n_total > 0: | |
| x_a = n_ratio_a / n_total | |
| x_b = n_ratio_b / n_total | |
| else: | |
| x_a = x_b = 0 | |
| return (x_a, x_b), None, (w_a, w_b) | |
| elif mode == lang["mode_vol_to_mass"]: | |
| phi_a, phi_b = input_val, 1.0 - input_val | |
| mass_a = phi_a * rho_a | |
| mass_b = phi_b * rho_b | |
| mass_total = mass_a + mass_b | |
| if mass_total > 0: | |
| w_a = mass_a / mass_total | |
| w_b = mass_b / mass_total | |
| else: | |
| w_a = w_b = 0 | |
| return None, (phi_a, phi_b), (w_a, w_b) | |
| elif mode == lang["mode_mass_to_vol"]: | |
| w_a, w_b = input_val, 1.0 - input_val | |
| vol_a = w_a / rho_a | |
| vol_b = w_b / rho_b | |
| vol_total = vol_a + vol_b | |
| if vol_total > 0: | |
| phi_a = vol_a / vol_total | |
| phi_b = vol_b / vol_total | |
| else: | |
| phi_a = phi_b = 0 | |
| return None, (phi_a, phi_b), (w_a, w_b) | |
| # 输入区域 | |
| with col1: | |
| # 初始化 input_val 默认值 | |
| input_val = 0.5 | |
| if conversion_mode in [lang["mode_mole_to_vol"], lang["mode_mole_to_mass"]]: | |
| st.markdown(f"### {lang['input_mole_fraction']}") | |
| input_val = st.number_input( | |
| f"{lang['mole_fraction_of']} {solvent_a_name} (x_A):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=0.5, | |
| step=0.001, | |
| format="%.4f", | |
| help="输入溶剂A的摩尔分数" | |
| ) | |
| complement_val = 1.0 - input_val | |
| st.metric( | |
| f"{lang['mole_fraction_of']} {solvent_b_name} (x_B)", | |
| f"{complement_val:.4f}", | |
| help="溶剂B的摩尔分数(自动计算)" | |
| ) | |
| elif conversion_mode in [lang["mode_vol_to_mole"], lang["mode_vol_to_mass"]]: | |
| st.markdown(f"### {lang['input_volume_fraction']}") | |
| input_val = st.number_input( | |
| f"{lang['volume_fraction_of']} {solvent_a_name} (φ_A):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=0.5, | |
| step=0.001, | |
| format="%.4f", | |
| help="输入溶剂A的体积分数" | |
| ) | |
| complement_val = 1.0 - input_val | |
| st.metric( | |
| f"{lang['volume_fraction_of']} {solvent_b_name} (φ_B)", | |
| f"{complement_val:.4f}", | |
| help="溶剂B的体积分数(自动计算)" | |
| ) | |
| elif conversion_mode in [lang["mode_mass_to_mole"], lang["mode_mass_to_vol"]]: | |
| st.markdown(f"### {lang['input_mass_fraction']}") | |
| input_val = st.number_input( | |
| f"{lang['mass_fraction_of']} {solvent_a_name} (w_A):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=0.5, | |
| step=0.001, | |
| format="%.4f", | |
| help="输入溶剂A的质量分数" | |
| ) | |
| complement_val = 1.0 - input_val | |
| st.metric( | |
| f"{lang['mass_fraction_of']} {solvent_b_name} (w_B)", | |
| f"{complement_val:.4f}", | |
| help="溶剂B的质量分数(自动计算)" | |
| ) | |
| # 计算结果 | |
| mole_result, vol_result, mass_result = calculate_conversion( | |
| conversion_mode, input_val, M_a, M_b, rho_a, rho_b | |
| ) | |
| # 结果显示区域 | |
| with col2: | |
| if conversion_mode == lang["mode_mole_to_vol"]: | |
| st.markdown(f"### {lang['result_volume_fraction']}") | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| st.metric( | |
| f"φ_A ({solvent_a_name})", | |
| f"{vol_result[0]:.4f}", | |
| delta=f"{vol_result[0] - mole_result[0]:.4f}", | |
| help="溶剂A的体积分数" | |
| ) | |
| with col2_2: | |
| st.metric( | |
| f"φ_B ({solvent_b_name})", | |
| f"{vol_result[1]:.4f}", | |
| delta=f"{vol_result[1] - mole_result[1]:.4f}", | |
| help="溶剂B的体积分数" | |
| ) | |
| elif conversion_mode == lang["mode_vol_to_mole"]: | |
| st.markdown(f"### {lang['result_mole_fraction']}") | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| st.metric( | |
| f"x_A ({solvent_a_name})", | |
| f"{mole_result[0]:.4f}", | |
| delta=f"{mole_result[0] - vol_result[0]:.4f}", | |
| help="溶剂A的摩尔分数" | |
| ) | |
| with col2_2: | |
| st.metric( | |
| f"x_B ({solvent_b_name})", | |
| f"{mole_result[1]:.4f}", | |
| delta=f"{mole_result[1] - vol_result[1]:.4f}", | |
| help="溶剂B的摩尔分数" | |
| ) | |
| elif conversion_mode == lang["mode_mole_to_mass"]: | |
| st.markdown(f"### {lang['result_mass_fraction']}") | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| st.metric( | |
| f"w_A ({solvent_a_name})", | |
| f"{mass_result[0]:.4f}", | |
| delta=f"{mass_result[0] - mole_result[0]:.4f}", | |
| help="溶剂A的质量分数" | |
| ) | |
| with col2_2: | |
| st.metric( | |
| f"w_B ({solvent_b_name})", | |
| f"{mass_result[1]:.4f}", | |
| delta=f"{mass_result[1] - mole_result[1]:.4f}", | |
| help="溶剂B的质量分数" | |
| ) | |
| elif conversion_mode == lang["mode_mass_to_mole"]: | |
| st.markdown(f"### {lang['result_mole_fraction']}") | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| st.metric( | |
| f"x_A ({solvent_a_name})", | |
| f"{mole_result[0]:.4f}", | |
| delta=f"{mole_result[0] - mass_result[0]:.4f}", | |
| help="溶剂A的摩尔分数" | |
| ) | |
| with col2_2: | |
| st.metric( | |
| f"x_B ({solvent_b_name})", | |
| f"{mole_result[1]:.4f}", | |
| delta=f"{mole_result[1] - mass_result[1]:.4f}", | |
| help="溶剂B的摩尔分数" | |
| ) | |
| elif conversion_mode == lang["mode_vol_to_mass"]: | |
| st.markdown(f"### {lang['result_mass_fraction']}") | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| st.metric( | |
| f"w_A ({solvent_a_name})", | |
| f"{mass_result[0]:.4f}", | |
| delta=f"{mass_result[0] - vol_result[0]:.4f}", | |
| help="溶剂A的质量分数" | |
| ) | |
| with col2_2: | |
| st.metric( | |
| f"w_B ({solvent_b_name})", | |
| f"{mass_result[1]:.4f}", | |
| delta=f"{mass_result[1] - vol_result[1]:.4f}", | |
| help="溶剂B的质量分数" | |
| ) | |
| elif conversion_mode == lang["mode_mass_to_vol"]: | |
| st.markdown(f"### {lang['result_volume_fraction']}") | |
| col2_1, col2_2 = st.columns(2) | |
| with col2_1: | |
| st.metric( | |
| f"φ_A ({solvent_a_name})", | |
| f"{vol_result[0]:.4f}", | |
| delta=f"{vol_result[0] - mass_result[0]:.4f}", | |
| help="溶剂A的体积分数" | |
| ) | |
| with col2_2: | |
| st.metric( | |
| f"φ_B ({solvent_b_name})", | |
| f"{vol_result[1]:.4f}", | |
| delta=f"{vol_result[1] - mass_result[1]:.4f}", | |
| help="溶剂B的体积分数" | |
| ) | |
| # 验证信息 | |
| st.markdown("---") | |
| if mole_result: | |
| st.success(f"✅ {lang['mole_verification']}: {mole_result[0] + mole_result[1]:.6f}") | |
| if vol_result: | |
| st.success(f"✅ {lang['volume_verification']}: {vol_result[0] + vol_result[1]:.6f}") | |
| if mass_result: | |
| st.success(f"✅ {lang['mass_verification']}: {mass_result[0] + mass_result[1]:.6f}") | |
| # 可视化部分修改 | |
| st.markdown("---") | |
| st.markdown(f"### {lang['visualization']}") | |
| # 创建饼图 | |
| fig = go.Figure() | |
| if mole_result: | |
| fig.add_trace(go.Pie( | |
| labels=[solvent_a_name, solvent_b_name], | |
| values=[mole_result[0], mole_result[1]], | |
| name=lang["mole_fraction_chart"], | |
| domain=dict(x=[0, 0.3]), | |
| title=lang["mole_fraction_chart"], | |
| marker_colors=[current_solvents.get(solvent_a_name, {}).get('color', '#3498db'), | |
| current_solvents.get(solvent_b_name, {}).get('color', '#e74c3c')] | |
| )) | |
| if vol_result: | |
| fig.add_trace(go.Pie( | |
| labels=[solvent_a_name, solvent_b_name], | |
| values=[vol_result[0], vol_result[1]], | |
| name=lang["volume_fraction_chart"], | |
| domain=dict(x=[0.35, 0.65]), | |
| title=lang["volume_fraction_chart"], | |
| marker_colors=[current_solvents.get(solvent_a_name, {}).get('color', '#3498db'), | |
| current_solvents.get(solvent_b_name, {}).get('color', '#e74c3c')] | |
| )) | |
| if mass_result: | |
| fig.add_trace(go.Pie( | |
| labels=[solvent_a_name, solvent_b_name], | |
| values=[mass_result[0], mass_result[1]], | |
| name=lang["mass_fraction_chart"], | |
| domain=dict(x=[0.7, 1.0]), | |
| title=lang["mass_fraction_chart"], | |
| marker_colors=[current_solvents.get(solvent_a_name, {}).get('color', '#3498db'), | |
| current_solvents.get(solvent_b_name, {}).get('color', '#e74c3c')] | |
| )) | |
| fig.update_traces(textposition='inside', textinfo='percent+label') | |
| fig.update_layout( | |
| title_text=lang["fraction_visualization"], | |
| showlegend=False, | |
| height=400, | |
| margin=dict(l=20, r=20, t=50, b=20) | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # 详细信息表格 | |
| st.markdown("---") | |
| st.markdown(f"### {lang['detailed_info']}") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.markdown(f"#### {lang['solvent_params']}") | |
| df_params = pd.DataFrame({ | |
| lang["solvent"]: [solvent_a_name, solvent_b_name], | |
| f"{lang['molar_mass']} (g/mol)": [M_a, M_b], | |
| f"{lang['density']} (g/cm³)": [rho_a, rho_b] | |
| }) | |
| st.dataframe(df_params, use_container_width=True) | |
| with col2: | |
| if mole_result: | |
| st.markdown(f"#### {lang['mole_fraction']}") | |
| df_mole = pd.DataFrame({ | |
| lang["solvent"]: [solvent_a_name, solvent_b_name], | |
| lang["mole_fraction"]: [f"{mole_result[0]:.4f}", f"{mole_result[1]:.4f}"] | |
| }) | |
| st.dataframe(df_mole, use_container_width=True) | |
| if vol_result: | |
| st.markdown(f"#### {lang['volume_fraction']}") | |
| df_vol = pd.DataFrame({ | |
| lang["solvent"]: [solvent_a_name, solvent_b_name], | |
| lang["volume_fraction"]: [f"{vol_result[0]:.4f}", f"{vol_result[1]:.4f}"] | |
| }) | |
| st.dataframe(df_vol, use_container_width=True) | |
| with col3: | |
| if mass_result: | |
| st.markdown(f"#### {lang['mass_fraction']}") | |
| df_mass = pd.DataFrame({ | |
| lang["solvent"]: [solvent_a_name, solvent_b_name], | |
| lang["mass_fraction"]: [f"{mass_result[0]:.4f}", f"{mass_result[1]:.4f}"] | |
| }) | |
| st.dataframe(df_mass, use_container_width=True) | |
| # 公式说明(在可折叠容器中) | |
| with st.expander(f"📐 {lang['calculation_formula']}", expanded=False): | |
| formula_dict = { | |
| lang["mode_mole_to_vol"]: { | |
| "formula": r"\phi_A = \frac{x_A \cdot M_A / \rho_A}{x_A \cdot M_A / \rho_A + x_B \cdot M_B / \rho_B}", | |
| "vars": "x_A, x_B: 摩尔分数; φ_A, φ_B: 体积分数; M_A, M_B: 摩尔质量 (g/mol); ρ_A, ρ_B: 密度 (g/cm³)" | |
| }, | |
| lang["mode_vol_to_mole"]: { | |
| "formula": r"x_A = \frac{\phi_A \cdot \rho_A / M_A}{\phi_A \cdot \rho_A / M_A + \phi_B \cdot \rho_B / M_B}", | |
| "vars": "φ_A, φ_B: 体积分数; x_A, x_B: 摩尔分数; M_A, M_B: 摩尔质量 (g/mol); ρ_A, ρ_B: 密度 (g/cm³)" | |
| }, | |
| lang["mode_mole_to_mass"]: { | |
| "formula": r"w_A = \frac{x_A \cdot M_A}{x_A \cdot M_A + x_B \cdot M_B}", | |
| "vars": "x_A, x_B: 摩尔分数; w_A, w_B: 质量分数; M_A, M_B: 摩尔质量 (g/mol)" | |
| }, | |
| lang["mode_mass_to_mole"]: { | |
| "formula": r"x_A = \frac{w_A / M_A}{w_A / M_A + w_B / M_B}", | |
| "vars": "w_A, w_B: 质量分数; x_A, x_B: 摩尔分数; M_A, M_B: 摩尔质量 (g/mol)" | |
| }, | |
| lang["mode_vol_to_mass"]: { | |
| "formula": r"w_A = \frac{\phi_A \cdot \rho_A}{\phi_A \cdot \rho_A + \phi_B \cdot \rho_B}", | |
| "vars": "φ_A, φ_B: 体积分数; w_A, w_B: 质量分数; ρ_A, ρ_B: 密度 (g/cm³)" | |
| }, | |
| lang["mode_mass_to_vol"]: { | |
| "formula": r"\phi_A = \frac{w_A / \rho_A}{w_A / \rho_A + w_B / \rho_B}", | |
| "vars": "w_A, w_B: 质量分数; φ_A, φ_B: 体积分数; ρ_A, ρ_B: 密度 (g/cm³)" | |
| } | |
| } | |
| if conversion_mode in formula_dict: | |
| st.latex(formula_dict[conversion_mode]["formula"]) | |
| st.markdown(f"**{lang['formula_where']}** {formula_dict[conversion_mode]['vars']}") | |
| # 使用说明 | |
| with st.expander(f"📖 {lang['usage_instructions']}", expanded=False): | |
| st.markdown(f""" | |
| {lang["instruction_1"]} | |
| {lang["instruction_2"]} | |
| {lang["instruction_3"]} | |
| {lang["instruction_4"]} | |
| """) | |
| # 注意事项 | |
| st.markdown(f""" | |
| <div class="warning-box"> | |
| {lang["note"]} | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # 页脚 | |
| st.markdown("---") | |
| st.markdown(""" | |
| <div style="text-align: center; color: #7f8c8d; padding: 1rem;"> | |
| <small>🧪 溶剂分数转换器 | 基于理想混合理论 | Made with Streamlit</small> | |
| </div> | |
| """, unsafe_allow_html=True) |