Solvent_Fraction_Convertor / src /streamlit_app.py
clidx's picture
Update src/streamlit_app.py
35ea9d3 verified
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)