This commit is contained in:
2025-08-20 10:27:38 +08:00
parent 9c92a3a449
commit 78b82192a1
2 changed files with 77 additions and 99 deletions

View File

@ -60,31 +60,6 @@ def calculate_molecular_weight_from_smiles(smiles):
st.error(f"SMILES分子量计算错误: {str(e)}") st.error(f"SMILES分子量计算错误: {str(e)}")
return None return None
def generate_molecule_image(inchi=None, smiles=None):
"""从SMILES生成分子结构图"""
return None
try:
if inchi:
mol = Chem.MolFromInchi(inchi)
elif smiles:
mol = Chem.MolFromSmiles(smiles)
else:
st.error("必须提供InChI或SMILES字符串")
return None
if mol:
# 生成分子图像
img = Draw.MolToImage(mol, size=(300, 300))
# 将图像转换为字节流
img_buffer = BytesIO()
img.save("a.png")
img.save(img_buffer, format='PNG')
img_buffer.seek(0)
return img_buffer
else:
return None
except Exception as e:
st.error(f"分子结构图生成错误: {str(e)}")
return None
def get_pubchem_properties(compound): def get_pubchem_properties(compound):
"""从PubChem获取密度、熔点、沸点信息""" """从PubChem获取密度、熔点、沸点信息"""
@ -144,16 +119,6 @@ def get_pubchem_properties(compound):
'boiling_point': None 'boiling_point': None
} }
def is_liquid_at_room_temp(melting_point):
"""判断常温下是否为液体假设常温为25°C"""
if melting_point is None:
return False
try:
mp = float(melting_point)
return mp < 25 # 熔点低于25°C认为是液体
except:
return False
def sync_calculations(compound_data, mmol=None, mass=None, volume=None, changed_field=None): def sync_calculations(compound_data, mmol=None, mass=None, volume=None, changed_field=None):
"""同步计算mmol、质量、体积""" """同步计算mmol、质量、体积"""
if not compound_data: if not compound_data:
@ -315,17 +280,6 @@ with col2:
st.markdown("### 其他数据") st.markdown("### 其他数据")
st.page_link(f"https://pubchem.ncbi.nlm.nih.gov/compound/{data['cid']}",label="**访问PubChem**") st.page_link(f"https://pubchem.ncbi.nlm.nih.gov/compound/{data['cid']}",label="**访问PubChem**")
st.image(f"https://pubchem.ncbi.nlm.nih.gov/image/imgsrv.fcgi?cid={data['cid']}&t=s","结构式") st.image(f"https://pubchem.ncbi.nlm.nih.gov/image/imgsrv.fcgi?cid={data['cid']}&t=s","结构式")
# st.button("访问PubChem",on_click=lambda :st.dire)
# if data['melting_point']:
# st.metric("熔点 (°C)", data['melting_point'])
# # 显示分子结构图
# if data.get('inchi') or data.get('smiles'):
# st.markdown("分子结构图")
# mol_img = generate_molecule_image(inchi=data['inchi'], smiles=data['smiles'])
# if mol_img:
# st.image(mol_img, caption="分子键线式结构图", width=150)
# else:
# st.info("无法生成分子结构图")
# 添加熔沸点信息的展开区域 # 添加熔沸点信息的展开区域
if data.get('melting_point_src') or data.get('boiling_point_src'): if data.get('melting_point_src') or data.get('boiling_point_src'):
@ -357,9 +311,6 @@ with col2:
melting_point = re.search(r'\d*\.\d+', melting_data[0]) melting_point = re.search(r'\d*\.\d+', melting_data[0])
if melting_point: if melting_point:
melting_point = float(melting_point.group()) melting_point = float(melting_point.group())
is_liquid = is_liquid_at_room_temp(melting_point)
else:
is_liquid = False
# 检测值变化并执行计算 # 检测值变化并执行计算
def handle_change(field_name, new_value, current_value): def handle_change(field_name, new_value, current_value):
@ -427,10 +378,7 @@ with col2:
# 密度显示选项 # 密度显示选项
show_density = False show_density = False
if data['density_src']: if data['density_src']:
if is_liquid: show_density = st.checkbox("显示密度信息", value=False)
show_density = st.checkbox("显示密度信息", value=True)
else:
show_density = st.checkbox("显示密度信息", value=False)
if show_density: if show_density:
import re import re

View File

@ -203,7 +203,38 @@ def calculate_properties(molecular_weight: float, amount_mmol: Optional[float] =
def reaction_table_page(): def reaction_table_page():
"""反应表格页面""" """反应表格页面"""
st.header("⚗️ 反应表格") st.header("反应表格")
with st.expander("**使用说明**", expanded=False):
st.markdown('''在下方的表格中您可以输入反应数据程序会尝试计算其余各项。当量为0时该物质不参与当量计算。
### 立即计算
选中`立即计算`选项后,输入后立即尝试计算修改部分,您可以实时查看计算结果。但请注意,请勿同时修改多项内容。如果输入某项后计算未正确进行,这是潜在的缺陷,请反馈。
不保证在您主动修改计算结果后程序能正确处理(例如,程序算出质量后您主动修改),通常这会伴随着警告,但也有可能遗漏某些情况。
### 延迟计算
不选中`立即计算`后,在完成输入后,您可以点击“计算”按钮,程序会尝试计算所有可用的结果。
程序在计算时会检查是否有冗余数据,例如同时给定分子量、用量、质量,并拒绝在这种情况下计算。您可以选中`不使用检查`选项来禁用检查,但请保证数据自洽(即使得所有已知量之间的关系成立),否则会发生未定义行为。
如果计算后您想要修改或补充内容再次计算,请选中`不使用检查`选项。
''')
with st.expander("关于未定义行为"):
st.markdown('''程序内部计算顺序为
1. n -> mass
2. mass -> n
3. mass -> volumn
4. volumn -> mass
5. mass -> n
6. eqiv -> n
7. n -> mass
8. mass -> volumn
如果存在不自洽的数据,您可以通过上述内容预测程序行为。但这不应该在实际使用中依赖,因为在不同版本下实现可能发生变化。
''')
# 初始化数据 # 初始化数据
if 'reaction_data' not in st.session_state: if 'reaction_data' not in st.session_state:
@ -222,12 +253,11 @@ def reaction_table_page():
st.session_state.reaction_data = df st.session_state.reaction_data = df
st.write("### 反应物质表格") st.info(f"💡 当量为0时该物质不参与当量计算。")
use_on_change = st.checkbox("是否立即计算", value=True) use_on_change = st.checkbox("立即计算", value=True)
st.info(f"💡 当量为0时该物质不参与当量计算。"+("如果保证表格已有内容自洽,可选中不使用检查。" if not use_on_change else ""))
if not use_on_change: if not use_on_change:
st.button("计算",on_click=calc_reaction_data)
st.checkbox("不使用检查", value=False,key="no_check",) st.checkbox("不使用检查", value=False,key="no_check",)
st.button("计算",on_click=calc_reaction_data,type="primary")
# 使用data_editor创建可编辑表格 # 使用data_editor创建可编辑表格
edited_data = st.data_editor( edited_data = st.data_editor(
@ -359,7 +389,17 @@ def calc_reaction_data():
fil = df["rho"].notna() & df["mass"].notna() & eqfil fil = df["rho"].notna() & df["mass"].notna() & eqfil
df.loc[fil, "vol"] = df[fil]["mass"] / df[fil]["rho"] # g -> mL再除以 g/mL df.loc[fil, "vol"] = df[fil]["mass"] / df[fil]["rho"] # g -> mL再除以 g/mL
df.columns = [
except Exception as e:
# if "df" in locals():
# st.session_state.reaction_data = df
st.error("计算过程中出错,表格可能有误")
print("calc_reaction_data error:", traceback.format_exc())
return
finally:
if "df" in locals():
df.columns = [
"物质", "物质",
"分子量", "分子量",
"当量", "当量",
@ -368,15 +408,8 @@ def calc_reaction_data():
"密度(g/mL)", "密度(g/mL)",
"体积(mL)", "体积(mL)",
"备注" "备注"
] ]
st.session_state.reaction_data = df
except Exception as e:
if "df" in locals():
st.session_state.reaction_data = df st.session_state.reaction_data = df
st.error("计算过程中出错,表格可能有误")
print("calc_reaction_data error:", traceback.format_exc())
return
def recalculate_reaction_data(): def recalculate_reaction_data():
"""根据最近一次编辑的行及当量,推算其他未编辑行的用量,并更新质量/体积。""" """根据最近一次编辑的行及当量,推算其他未编辑行的用量,并更新质量/体积。"""
@ -579,8 +612,31 @@ def add_compound_to_reaction(compound:PubChemCompound):
def compound_search_page(): def compound_search_page():
"""化合物搜索页面""" """化合物搜索页面"""
# 输入区域 # 输入区域
st.header("📝 输入查询条件")
with st.expander("**使用说明**"):
st.markdown("""
1. **选择搜索类型**:
- 名称: 输入化合物的常用名称或IUPAC名称
- 化学式: 输入分子式 (如 C2H6O)
- SMILES: 输入SMILES字符串 (如 CCO)
2. **输入查询条件**: 在输入框中输入相应的查询词
3. **点击搜索**: 系统将从PubChem数据库中查询匹配的化合物
4. **查看结果**:
- 基本信息包括名称、化学式、分子量和2D结构图
- 密度和熔沸点信息可在展开区域查看
- 计算器可帮助您计算用量、质量和体积的关系
### 示例查询
- **名称**: ethanol, water, glucose
- **化学式**: C2H6O, H2O, C6H12O6
- **SMILES**: CCO, O, C(C1C(C(C(C(O1)O)O)O)O)O
""")
st.header("查询条件")
col1, col2 = st.columns([1, 2]) col1, col2 = st.columns([1, 2])
with col1: with col1:
@ -803,44 +859,18 @@ def compound_search_page():
else: else:
st.warning("无分子量数据,无法进行计算") st.warning("无分子量数据,无法进行计算")
else:
# 显示使用说明
st.info("👈 请在左侧输入化合物信息开始查询")
st.markdown("""
### 🔍 使用说明
1. **选择搜索类型**:
- 名称: 输入化合物的常用名称或IUPAC名称
- 化学式: 输入分子式 (如 C2H6O)
- SMILES: 输入SMILES字符串 (如 CCO)
2. **输入查询条件**: 在输入框中输入相应的查询词
3. **点击搜索**: 系统将从PubChem数据库中查询匹配的化合物
4. **查看结果**:
- 基本信息包括名称、化学式、分子量和2D结构图
- 密度和熔沸点信息可在展开区域查看
- 计算器可帮助您计算用量、质量和体积的关系
### 📝 示例查询
- **名称**: ethanol, water, glucose
- **化学式**: C2H6O, H2O, C6H12O6
- **SMILES**: CCO, O, C(C1C(C(C(C(O1)O)O)O)O)O
""")
def main(): def main():
st.set_page_config( st.set_page_config(
page_title="PubChem化合物查询工具", page_title="有机合成用量计算工具",
page_icon="🧪", page_icon="🧪",
layout="wide" layout="wide"
) )
# 侧边栏导航 # 侧边栏导航
with st.sidebar: with st.sidebar:
st.title("🧪 化学工具")
page = st.radio( page = st.radio(
"选择功能页面", "选择功能页面",
["化合物查询", "反应表格"], ["化合物查询", "反应表格"],