Spaces:
Build error
Build error
Upload 3 files
Browse files- README.md +36 -14
- app (1).py +299 -0
- requirements (1).txt +4 -0
README.md
CHANGED
|
@@ -1,14 +1,36 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# 🧪 Interactive Periodic Table (Gradio)
|
| 3 |
+
|
| 4 |
+
A lightweight, click-to-explore periodic table you can deploy to a **Hugging Face Space**.
|
| 5 |
+
|
| 6 |
+
**Features**
|
| 7 |
+
- Click any element to view properties (mass, density, melting/boiling points, radii, etc.).
|
| 8 |
+
- Search by **symbol, name, or atomic number**.
|
| 9 |
+
- **Trend chart** for the selected element.
|
| 10 |
+
- **Periodic heatmap** for a chosen property (e.g., electronegativity).
|
| 11 |
+
- A couple of **fun facts** per element (group-based + curated highlights).
|
| 12 |
+
|
| 13 |
+
**Tech**
|
| 14 |
+
- [Gradio](https://www.gradio.app/) UI
|
| 15 |
+
- [`periodictable`](https://pypi.org/project/periodictable/) for element data
|
| 16 |
+
- `plotly` for charts
|
| 17 |
+
|
| 18 |
+
## Deploy on Hugging Face Spaces
|
| 19 |
+
|
| 20 |
+
1. Create a new Space → **Gradio** template.
|
| 21 |
+
2. Add these files at the root of the repo:
|
| 22 |
+
- `app.py`
|
| 23 |
+
- `requirements.txt`
|
| 24 |
+
- `README.md` (optional)
|
| 25 |
+
3. Commit. The Space will build and launch. If you see a build error, open the **Logs** tab.
|
| 26 |
+
|
| 27 |
+
**Tip:** Spaces sometimes sleep on free tiers; first load may take a bit while dependencies install.
|
| 28 |
+
|
| 29 |
+
## Local development
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
pip install -r requirements.txt
|
| 33 |
+
python app.py
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
Then open the local Gradio URL in your browser.
|
app (1).py
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import math
|
| 3 |
+
from dataclasses import asdict, dataclass
|
| 4 |
+
from typing import Dict, List, Optional, Tuple
|
| 5 |
+
|
| 6 |
+
import gradio as gr
|
| 7 |
+
import pandas as pd
|
| 8 |
+
import plotly.express as px
|
| 9 |
+
|
| 10 |
+
# External dependency:
|
| 11 |
+
# pip install periodictable
|
| 12 |
+
from periodictable import elements
|
| 13 |
+
|
| 14 |
+
# -----------------------------
|
| 15 |
+
# Data extraction helpers
|
| 16 |
+
# -----------------------------
|
| 17 |
+
NUMERIC_PROPS = [
|
| 18 |
+
("mass", "Atomic mass (u)"),
|
| 19 |
+
("density", "Density (g/cm^3)"),
|
| 20 |
+
("electronegativity", "Pauling electronegativity"),
|
| 21 |
+
("boiling_point", "Boiling point (K)"),
|
| 22 |
+
("melting_point", "Melting point (K)"),
|
| 23 |
+
("vdw_radius", "van der Waals radius (pm)"),
|
| 24 |
+
("covalent_radius", "Covalent radius (pm)"),
|
| 25 |
+
]
|
| 26 |
+
|
| 27 |
+
# Some curated quick facts. We'll augment with group-based facts so every element gets at least one.
|
| 28 |
+
CURATED_FACTS: Dict[str, List[str]] = {
|
| 29 |
+
"H": ["Lightest element; ~74% of the visible universe by mass is hydrogen in stars."],
|
| 30 |
+
"He": ["Inert, used in cryogenics and balloons; second lightest element."],
|
| 31 |
+
"Li": ["Batteries MVP: lithium-ion cells power phones and EVs."],
|
| 32 |
+
"C": ["Backbone of life; diamond and graphite are pure carbon with wildly different properties."],
|
| 33 |
+
"N": ["~78% of Earth's atmosphere is nitrogen (mostly N₂)."],
|
| 34 |
+
"O": ["Essential for respiration; ~21% of Earth's atmosphere."],
|
| 35 |
+
"Na": ["Sodium metal reacts violently with water—handle only under oil or inert gas."],
|
| 36 |
+
"Mg": ["Burns with a bright white flame; used in flares and fireworks."],
|
| 37 |
+
"Al": ["Light and strong; forms a protective oxide layer that resists corrosion."],
|
| 38 |
+
"Si": ["Silicon is the basis of modern electronics—hello, semiconductors."],
|
| 39 |
+
"Cl": ["Powerful disinfectant; elemental chlorine is toxic, compounds are widely useful."],
|
| 40 |
+
"Ar": ["Argon is used to provide inert atmospheres for welding and 3D printing."],
|
| 41 |
+
"Fe": ["Core of steel; iron is essential in hemoglobin for oxygen transport."],
|
| 42 |
+
"Cu": ["Excellent electrical conductor; iconic blue-green patina (verdigris)."],
|
| 43 |
+
"Ag": ["Highest electrical conductivity of all metals; historically used as currency."],
|
| 44 |
+
"Au": ["Very unreactive ('noble'); prized for electronics and jewelry."],
|
| 45 |
+
"Hg": ["Only metal that's liquid at room temperature; toxic—use with care."],
|
| 46 |
+
"Pb": ["Dense and malleable; toxicity led to phase-out from gasoline and paints."],
|
| 47 |
+
"U": ["Radioactive; used as nuclear reactor fuel (U-235)."],
|
| 48 |
+
"Pu": ["Man-made in quantity; key in certain nuclear technologies."],
|
| 49 |
+
"F": ["Most electronegative element; extremely reactive."],
|
| 50 |
+
"Ne": ["Neon glows striking red-orange in discharge tubes—classic signs."],
|
| 51 |
+
"Xe": ["Xenon makes bright camera flashes and high-intensity lamps."],
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
GROUP_FACTS = {
|
| 55 |
+
"alkali": "Alkali metal: very reactive soft metal; forms +1 cations and reacts with water.",
|
| 56 |
+
"alkaline-earth": "Alkaline earth metal: reactive (less than Group 1); forms +2 cations.",
|
| 57 |
+
"transition": "Transition metal: often good catalysts, colorful compounds, multiple oxidation states.",
|
| 58 |
+
"post-transition": "Post-transition metal: softer metals with lower melting points than transition metals.",
|
| 59 |
+
"metalloid": "Metalloid: properties between metals and nonmetals; often semiconductors.",
|
| 60 |
+
"nonmetal": "Nonmetal: tends to form covalent compounds; wide range of roles in biology and materials.",
|
| 61 |
+
"halogen": "Halogen: very reactive nonmetals; form salts with metals and −1 oxidation state.",
|
| 62 |
+
"noble-gas": "Noble gas: chemically inert under most conditions; monatomic gases.",
|
| 63 |
+
"lanthanide": "Lanthanide: f-block rare earths; notable for magnets, lasers, and phosphors.",
|
| 64 |
+
"actinide": "Actinide: radioactive f-block; includes nuclear fuel materials.",
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
# Map periodictable categories into the above buckets
|
| 68 |
+
def classify_category(el) -> str:
|
| 69 |
+
try:
|
| 70 |
+
if el.block == "s" and el.group == 1 and el.number != 1:
|
| 71 |
+
return "alkali"
|
| 72 |
+
if el.block == "s" and el.group == 2:
|
| 73 |
+
return "alkaline-earth"
|
| 74 |
+
if el.block == "p" and el.group in (13, 14, 15, 16) and el.metallic:
|
| 75 |
+
return "post-transition"
|
| 76 |
+
if el.block == "d":
|
| 77 |
+
return "transition"
|
| 78 |
+
if el.block == "p" and el.group == 17:
|
| 79 |
+
return "halogen"
|
| 80 |
+
if el.block == "p" and el.group == 18:
|
| 81 |
+
return "noble-gas"
|
| 82 |
+
if el.block == "p" and not el.metallic:
|
| 83 |
+
return "nonmetal"
|
| 84 |
+
if el.block == "f" and 57 <= el.number <= 71:
|
| 85 |
+
return "lanthanide"
|
| 86 |
+
if el.block == "f" and 89 <= el.number <= 103:
|
| 87 |
+
return "actinide"
|
| 88 |
+
except Exception:
|
| 89 |
+
pass
|
| 90 |
+
return "nonmetal" if not getattr(el, "metallic", False) else "post-transition"
|
| 91 |
+
|
| 92 |
+
# Build a dataframe of elements
|
| 93 |
+
def build_elements_df() -> pd.DataFrame:
|
| 94 |
+
rows = []
|
| 95 |
+
for Z in range(1, 119):
|
| 96 |
+
el = elements[Z]
|
| 97 |
+
if el is None:
|
| 98 |
+
continue
|
| 99 |
+
data = {
|
| 100 |
+
"Z": el.number,
|
| 101 |
+
"symbol": el.symbol,
|
| 102 |
+
"name": el.name.title(),
|
| 103 |
+
"period": getattr(el, "period", None),
|
| 104 |
+
"group": getattr(el, "group", None),
|
| 105 |
+
"block": getattr(el, "block", None),
|
| 106 |
+
"mass": getattr(el, "mass", None),
|
| 107 |
+
"density": getattr(el, "density", None),
|
| 108 |
+
"electronegativity": getattr(el, "electronegativity", None),
|
| 109 |
+
"boiling_point": getattr(el, "boiling_point", None),
|
| 110 |
+
"melting_point": getattr(el, "melting_point", None),
|
| 111 |
+
"vdw_radius": getattr(el, "vdw_radius", None),
|
| 112 |
+
"covalent_radius": getattr(el, "covalent_radius", None),
|
| 113 |
+
"category": classify_category(el),
|
| 114 |
+
"is_radioactive": bool(getattr(el, "radioactive", False)),
|
| 115 |
+
}
|
| 116 |
+
rows.append(data)
|
| 117 |
+
df = pd.DataFrame(rows).sort_values("Z").reset_index(drop=True)
|
| 118 |
+
return df
|
| 119 |
+
|
| 120 |
+
DF = build_elements_df()
|
| 121 |
+
|
| 122 |
+
# Layout positions: group (1-18) x period (1-7); f-block as two rows
|
| 123 |
+
MAX_GROUP = 18
|
| 124 |
+
MAX_PERIOD = 7
|
| 125 |
+
|
| 126 |
+
GRID: List[List[Optional[int]]] = [[None for _ in range(MAX_GROUP)] for _ in range(MAX_PERIOD)]
|
| 127 |
+
for _, row in DF.iterrows():
|
| 128 |
+
period, group, Z = int(row["period"]), row["group"], int(row["Z"])
|
| 129 |
+
if group is None:
|
| 130 |
+
continue
|
| 131 |
+
GRID[period-1][group-1] = Z
|
| 132 |
+
|
| 133 |
+
# f-block positions (lanthanides/actinides) - show in separate rows
|
| 134 |
+
LAN = [z for z in DF["Z"] if 57 <= z <= 71]
|
| 135 |
+
ACT = [z for z in DF["Z"] if 89 <= z <= 103]
|
| 136 |
+
|
| 137 |
+
# -----------------------------
|
| 138 |
+
# UI callbacks
|
| 139 |
+
# -----------------------------
|
| 140 |
+
|
| 141 |
+
def element_info(z_or_symbol: str):
|
| 142 |
+
# Accept atomic number or symbol
|
| 143 |
+
try:
|
| 144 |
+
if z_or_symbol.isdigit():
|
| 145 |
+
Z = int(z_or_symbol)
|
| 146 |
+
el = elements[Z]
|
| 147 |
+
else:
|
| 148 |
+
el = elements.symbol(z_or_symbol)
|
| 149 |
+
Z = el.number
|
| 150 |
+
except Exception:
|
| 151 |
+
return f\"Unknown element: {z_or_symbol}\", None, None
|
| 152 |
+
|
| 153 |
+
row = DF.loc[DF['Z'] == Z].iloc[0].to_dict()
|
| 154 |
+
symbol = row['symbol']
|
| 155 |
+
# Build facts
|
| 156 |
+
facts = []
|
| 157 |
+
facts.extend(CURATED_FACTS.get(symbol, []))
|
| 158 |
+
facts.append(GROUP_FACTS.get(row['category'], None))
|
| 159 |
+
facts = [f for f in facts if f]
|
| 160 |
+
|
| 161 |
+
# Properties text
|
| 162 |
+
props_lines = [
|
| 163 |
+
f\"{row['name']} ({symbol}), Z = {Z}\",
|
| 164 |
+
f\"Period {int(row['period'])}, Group {row['group']}, Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}\",
|
| 165 |
+
f\"Atomic mass: {row['mass'] if row['mass'] else '—'} u\",
|
| 166 |
+
f\"Density: {row['density'] if row['density'] else '—'} g/cm³\",
|
| 167 |
+
f\"Electronegativity: {row['electronegativity'] if row['electronegativity'] else '—'} (Pauling)\",
|
| 168 |
+
f\"Melting point: {row['melting_point'] if row['melting_point'] else '—'} K | Boiling point: {row['boiling_point'] if row['boiling_point'] else '—'} K\",
|
| 169 |
+
f\"vdW radius: {row['vdw_radius'] if row['vdw_radius'] else '—'} pm | Covalent radius: {row['covalent_radius'] if row['covalent_radius'] else '—'} pm\",
|
| 170 |
+
f\"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}\",
|
| 171 |
+
]
|
| 172 |
+
info_text = \"\\n\".join(props_lines)
|
| 173 |
+
facts_text = \"\\n• \".join([\"Interesting facts:\"] + facts) if facts else \"No fact on file—still cool though!\"
|
| 174 |
+
|
| 175 |
+
# Trend plot (Atomic number vs selected property)
|
| 176 |
+
# We'll default to electronegativity if available, else mass.
|
| 177 |
+
prop_key = 'electronegativity' if not pd.isna(row['electronegativity']) else 'mass'
|
| 178 |
+
label = dict(NUMERIC_PROPS)[prop_key]
|
| 179 |
+
trend_df = DF[['Z', 'symbol', prop_key]].dropna()
|
| 180 |
+
fig = px.scatter(
|
| 181 |
+
trend_df, x='Z', y=prop_key, hover_name='symbol', title=f'{label} across the periodic table',
|
| 182 |
+
)
|
| 183 |
+
# Highlight selected element
|
| 184 |
+
fig.add_scatter(x=[Z], y=[row[prop_key]] if row[prop_key] else [None],
|
| 185 |
+
mode='markers+text', text=[symbol], textposition='top center')
|
| 186 |
+
|
| 187 |
+
return info_text, facts_text, fig
|
| 188 |
+
|
| 189 |
+
def handle_button_click(z: int):
|
| 190 |
+
return element_info(str(z))
|
| 191 |
+
|
| 192 |
+
def search_element(query: str):
|
| 193 |
+
query = (query or '').strip()
|
| 194 |
+
if not query:
|
| 195 |
+
return gr.update(), gr.update(), gr.update()
|
| 196 |
+
return element_info(query)
|
| 197 |
+
|
| 198 |
+
def heatmap(property_key: str):
|
| 199 |
+
prop_label = dict(NUMERIC_PROPS)[property_key]
|
| 200 |
+
# Create a pseudo-2D matrix for the s/p/d blocks (7x18) with property values
|
| 201 |
+
import numpy as np
|
| 202 |
+
grid_vals = np.full((MAX_PERIOD, MAX_GROUP), None, dtype=object)
|
| 203 |
+
for r in range(MAX_PERIOD):
|
| 204 |
+
for c in range(MAX_GROUP):
|
| 205 |
+
z = GRID[r][c]
|
| 206 |
+
if z is None:
|
| 207 |
+
continue
|
| 208 |
+
val = DF.loc[DF['Z'] == z, property_key].values[0]
|
| 209 |
+
grid_vals[r, c] = val if not pd.isna(val) else None
|
| 210 |
+
|
| 211 |
+
fig = px.imshow(
|
| 212 |
+
grid_vals.astype(float),
|
| 213 |
+
origin='upper',
|
| 214 |
+
labels=dict(color=prop_label, x='Group', y='Period'),
|
| 215 |
+
x=list(range(1, MAX_GROUP+1)),
|
| 216 |
+
y=list(range(1, MAX_PERIOD+1)),
|
| 217 |
+
title=f'Periodic heatmap: {prop_label}',
|
| 218 |
+
aspect='auto',
|
| 219 |
+
color_continuous_scale='Viridis'
|
| 220 |
+
)
|
| 221 |
+
return fig
|
| 222 |
+
|
| 223 |
+
# -----------------------------
|
| 224 |
+
# Build UI
|
| 225 |
+
# -----------------------------
|
| 226 |
+
with gr.Blocks(title="Interactive Periodic Table", css=\"\"\"
|
| 227 |
+
.button-cell {min-width: 40px; height: 40px; padding: 0.25rem; font-weight: 600;}
|
| 228 |
+
.symbol {font-size: 0.95rem;}
|
| 229 |
+
.small {font-size: 0.7rem; opacity: 0.8;}
|
| 230 |
+
.grid {display: grid; grid-template-columns: repeat(18, 1fr); gap: 4px;}
|
| 231 |
+
.fgrid {display: grid; grid-template-columns: repeat(15, 1fr); gap: 4px;}
|
| 232 |
+
.header {text-align:center; font-weight:700; margin: 0.5rem 0;}
|
| 233 |
+
\"\"\") as demo:
|
| 234 |
+
gr.Markdown(\"# 🧪 Interactive Periodic Table\\nClick an element or search by symbol/name/atomic number.\")
|
| 235 |
+
|
| 236 |
+
with gr.Row():
|
| 237 |
+
with gr.Column(scale=2):
|
| 238 |
+
gr.Markdown(\"### Main Table\")
|
| 239 |
+
main_buttons = []
|
| 240 |
+
with gr.Group():
|
| 241 |
+
with gr.Row():
|
| 242 |
+
gr.HTML('<div class=\"grid\">' + ''.join([f'<div class=\"header\">{g}</div>' for g in range(1, 19)]) + '</div>')
|
| 243 |
+
# Build button grid
|
| 244 |
+
rows = []
|
| 245 |
+
for r in range(MAX_PERIOD):
|
| 246 |
+
with gr.Row():
|
| 247 |
+
cells = []
|
| 248 |
+
for c in range(MAX_GROUP):
|
| 249 |
+
z = GRID[r][c]
|
| 250 |
+
if z is None:
|
| 251 |
+
btn = gr.Button(\"\", elem_classes=[\"button-cell\"])
|
| 252 |
+
btn.click(lambda: (gr.update(), gr.update(), gr.update()))
|
| 253 |
+
else:
|
| 254 |
+
sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
|
| 255 |
+
btn = gr.Button(sym, elem_classes=[\"button-cell\"])
|
| 256 |
+
btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
|
| 257 |
+
gr.Textbox(interactive=False), gr.Markdown(), gr.Plot()])
|
| 258 |
+
cells.append(btn)
|
| 259 |
+
rows.append(cells)
|
| 260 |
+
gr.Markdown(\"### f-block (lanthanides & actinides)\")
|
| 261 |
+
with gr.Row():
|
| 262 |
+
# Lanthanides row
|
| 263 |
+
lan_buttons = []
|
| 264 |
+
for z in LAN:
|
| 265 |
+
sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
|
| 266 |
+
btn = gr.Button(sym, elem_classes=[\"button-cell\"])
|
| 267 |
+
btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
|
| 268 |
+
gr.Textbox(interactive=False), gr.Markdown(), gr.Plot()])
|
| 269 |
+
lan_buttons.append(btn)
|
| 270 |
+
with gr.Row():
|
| 271 |
+
# Actinides row
|
| 272 |
+
act_buttons = []
|
| 273 |
+
for z in ACT:
|
| 274 |
+
sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
|
| 275 |
+
btn = gr.Button(sym, elem_classes=[\"button-cell\"])
|
| 276 |
+
btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
|
| 277 |
+
gr.Textbox(interactive=False), gr.Markdown(), gr.Plot()])
|
| 278 |
+
act_buttons.append(btn)
|
| 279 |
+
|
| 280 |
+
with gr.Column(scale=1):
|
| 281 |
+
gr.Markdown(\"### Inspector\")
|
| 282 |
+
search = gr.Textbox(label=\"Search (symbol/name/Z)\", placeholder=\"e.g., C, Iron, 79\" )
|
| 283 |
+
info = gr.Textbox(label=\"Properties\", lines=10, interactive=False)
|
| 284 |
+
facts = gr.Markdown(\"Select an element to see fun facts.\")
|
| 285 |
+
trend = gr.Plot()
|
| 286 |
+
|
| 287 |
+
search.submit(search_element, inputs=[search], outputs=[info, facts, trend])
|
| 288 |
+
|
| 289 |
+
gr.Markdown(\"### Trend heatmap\")
|
| 290 |
+
prop = gr.Dropdown(choices=[k for k, _ in NUMERIC_PROPS], value=\"electronegativity\", label=\"Property\")
|
| 291 |
+
heat = gr.Plot()
|
| 292 |
+
prop.change(heatmap, inputs=[prop], outputs=[heat])
|
| 293 |
+
# Initialize
|
| 294 |
+
heat.update(heatmap(\"electronegativity\"))
|
| 295 |
+
|
| 296 |
+
gr.Markdown(\"---\\nBuilt with **Gradio** + **periodictable**. Data completeness varies by element; some values may be missing.\")
|
| 297 |
+
|
| 298 |
+
if __name__ == \"__main__\":
|
| 299 |
+
demo.launch()
|
requirements (1).txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.29.0
|
| 2 |
+
plotly>=5.22.0
|
| 3 |
+
pandas>=2.2.2
|
| 4 |
+
periodictable>=1.6.1
|