Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
@@ -1,32 +1,20 @@
|
|
1 |
-
import math
|
2 |
-
from typing import Dict, List, Optional
|
3 |
-
|
4 |
import gradio as gr
|
5 |
import pandas as pd
|
6 |
import numpy as np
|
7 |
import matplotlib.pyplot as plt
|
|
|
8 |
from periodictable import elements
|
9 |
|
10 |
-
#
|
11 |
-
# Helpers
|
12 |
-
# -----------------------------
|
13 |
def to_float(x):
|
14 |
-
"""Coerce periodictable numeric (incl. uncertainties) to plain float, else NaN."""
|
15 |
if x is None:
|
16 |
return np.nan
|
|
|
17 |
try:
|
18 |
-
# uncertainties.UFloat has .nominal_value
|
19 |
-
v = getattr(x, "nominal_value", x)
|
20 |
return float(v)
|
21 |
except Exception:
|
22 |
-
|
23 |
-
return float(x)
|
24 |
-
except Exception:
|
25 |
-
return np.nan
|
26 |
|
27 |
-
# -----------------------------
|
28 |
-
# Data
|
29 |
-
# -----------------------------
|
30 |
NUMERIC_PROPS = [
|
31 |
("mass", "Atomic mass (u)"),
|
32 |
("density", "Density (g/cm^3)"),
|
@@ -38,42 +26,40 @@ NUMERIC_PROPS = [
|
|
38 |
]
|
39 |
|
40 |
CURATED_FACTS: Dict[str, List[str]] = {
|
41 |
-
"H": ["Lightest element; ~74% of
|
42 |
-
"He": ["Inert
|
43 |
-
"Li": ["
|
44 |
-
"C": ["
|
45 |
-
"N": ["~78% of Earth's atmosphere is
|
46 |
-
"O": ["
|
47 |
-
"Na": ["
|
48 |
-
"Mg": ["
|
49 |
-
"
|
50 |
-
"
|
51 |
-
"
|
52 |
-
"
|
53 |
-
"
|
54 |
-
"
|
55 |
-
"
|
56 |
-
"
|
57 |
-
"
|
58 |
-
"
|
59 |
-
"
|
60 |
-
"
|
61 |
-
"
|
62 |
-
"Ne": ["Neon glows striking red-orange in discharge tubes—classic signs."],
|
63 |
-
"Xe": ["Xenon makes bright camera flashes and high-intensity lamps."],
|
64 |
}
|
65 |
|
66 |
GROUP_FACTS = {
|
67 |
-
"alkali": "Alkali metal: very reactive
|
68 |
-
"alkaline-earth": "Alkaline earth metal: reactive
|
69 |
-
"transition": "Transition metal:
|
70 |
-
"post-transition": "Post-transition metal: softer
|
71 |
-
"metalloid": "Metalloid:
|
72 |
-
"nonmetal": "Nonmetal:
|
73 |
-
"halogen": "Halogen: very reactive nonmetals;
|
74 |
-
"noble-gas": "Noble gas:
|
75 |
-
"lanthanide": "Lanthanide:
|
76 |
-
"actinide": "Actinide: radioactive
|
77 |
}
|
78 |
|
79 |
def classify_category(el) -> str:
|
@@ -82,12 +68,12 @@ def classify_category(el) -> str:
|
|
82 |
return "alkali"
|
83 |
if el.block == "s" and el.group == 2:
|
84 |
return "alkaline-earth"
|
|
|
|
|
85 |
if el.block == "p" and el.group == 17:
|
86 |
return "halogen"
|
87 |
if el.block == "p" and el.group == 18:
|
88 |
return "noble-gas"
|
89 |
-
if el.block == "d":
|
90 |
-
return "transition"
|
91 |
if el.block == "f" and 57 <= el.number <= 71:
|
92 |
return "lanthanide"
|
93 |
if el.block == "f" and 89 <= el.number <= 103:
|
@@ -106,12 +92,12 @@ def build_elements_df() -> pd.DataFrame:
|
|
106 |
el = elements[Z]
|
107 |
if el is None:
|
108 |
continue
|
109 |
-
|
110 |
"Z": el.number,
|
111 |
"symbol": el.symbol,
|
112 |
"name": el.name.title(),
|
113 |
"period": getattr(el, "period", None),
|
114 |
-
"group": getattr(el, "group", None),
|
115 |
"block": getattr(el, "block", None),
|
116 |
"mass": to_float(getattr(el, "mass", None)),
|
117 |
"density": to_float(getattr(el, "density", None)),
|
@@ -122,54 +108,35 @@ def build_elements_df() -> pd.DataFrame:
|
|
122 |
"covalent_radius": to_float(getattr(el, "covalent_radius", None)),
|
123 |
"category": classify_category(el),
|
124 |
"is_radioactive": bool(getattr(el, "radioactive", False)),
|
125 |
-
}
|
126 |
-
rows.append(data)
|
127 |
return pd.DataFrame(rows).sort_values("Z").reset_index(drop=True)
|
128 |
|
129 |
DF = build_elements_df()
|
130 |
|
131 |
-
#
|
132 |
-
#
|
133 |
-
|
134 |
-
#
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
GRID[0][0] = int(s[0]) # H -> group 1
|
150 |
-
if len(p) >= 1:
|
151 |
-
GRID[0][17] = int(p[-1]) # He -> group 18
|
152 |
-
continue
|
153 |
-
# s-block (usually 2)
|
154 |
-
if len(s) >= 1:
|
155 |
-
GRID[period - 1][0] = int(s[0]) # group 1
|
156 |
-
if len(s) >= 2:
|
157 |
-
GRID[period - 1][1] = int(s[1]) # group 2
|
158 |
-
# d-block (10 wide), only in periods >= 4
|
159 |
-
for i, z in enumerate(d):
|
160 |
-
if i < 10:
|
161 |
-
GRID[period - 1][2 + i] = int(z) # groups 3..12
|
162 |
-
# p-block (6 wide)
|
163 |
-
for i, z in enumerate(p[-6:]): # last 6 p-block in order
|
164 |
-
GRID[period - 1][12 + i] = int(z) # groups 13..18
|
165 |
|
166 |
-
# f-block lists (
|
167 |
-
LAN =
|
168 |
-
ACT =
|
169 |
|
170 |
-
#
|
171 |
-
# Plotting (Matplotlib -> gr.Plot)
|
172 |
-
# -----------------------------
|
173 |
def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
|
174 |
fig, ax = plt.subplots()
|
175 |
ax.scatter(trend_df["Z"], trend_df[prop_key])
|
@@ -185,9 +152,10 @@ def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
|
|
185 |
|
186 |
def plot_heatmap(property_key: str):
|
187 |
prop_label = dict(NUMERIC_PROPS)[property_key]
|
188 |
-
|
189 |
-
|
190 |
-
|
|
|
191 |
z = GRID[r][c]
|
192 |
if z is None:
|
193 |
continue
|
@@ -196,10 +164,10 @@ def plot_heatmap(property_key: str):
|
|
196 |
grid_vals[r, c] = float(val)
|
197 |
fig, ax = plt.subplots()
|
198 |
im = ax.imshow(grid_vals, origin="upper", aspect="auto")
|
199 |
-
ax.set_xticks(range(
|
200 |
-
ax.set_xticklabels([str(i) for i in range(1,
|
201 |
-
ax.set_yticks(range(
|
202 |
-
ax.set_yticklabels([str(i) for i in range(1,
|
203 |
ax.set_xlabel("Group")
|
204 |
ax.set_ylabel("Period")
|
205 |
ax.set_title(f"Periodic heatmap: {prop_label}")
|
@@ -207,9 +175,7 @@ def plot_heatmap(property_key: str):
|
|
207 |
fig.tight_layout()
|
208 |
return fig
|
209 |
|
210 |
-
#
|
211 |
-
# Callbacks
|
212 |
-
# -----------------------------
|
213 |
def element_info(z_or_symbol: str):
|
214 |
try:
|
215 |
if z_or_symbol.isdigit():
|
@@ -229,18 +195,19 @@ def element_info(z_or_symbol: str):
|
|
229 |
facts.append(GROUP_FACTS.get(row["category"], None))
|
230 |
facts = [f for f in facts if f]
|
231 |
|
|
|
|
|
|
|
232 |
props_lines = [
|
233 |
f"{row['name']} ({symbol}), Z = {Z}",
|
234 |
f"Period {int(row['period']) if not pd.isna(row['period']) else '—'}, "
|
235 |
f"Group {row['group'] if row['group'] is not None else '—'}, "
|
236 |
f"Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
|
237 |
-
f"Atomic mass: {
|
238 |
-
f"Density: {
|
239 |
-
f"Electronegativity: {
|
240 |
-
f"Melting point: {row['melting_point']
|
241 |
-
f"
|
242 |
-
f"vdW radius: {row['vdw_radius'] if not pd.isna(row['vdw_radius']) else '—'} pm | "
|
243 |
-
f"Covalent radius: {row['covalent_radius'] if not pd.isna(row['covalent_radius']) else '—'} pm",
|
244 |
f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
|
245 |
]
|
246 |
info_text = "\n".join(props_lines)
|
@@ -249,7 +216,6 @@ def element_info(z_or_symbol: str):
|
|
249 |
prop_key = "electronegativity" if not pd.isna(row["electronegativity"]) else "mass"
|
250 |
trend_df = DF[["Z", "symbol", prop_key]].dropna()
|
251 |
fig = plot_trend(trend_df, prop_key, Z, symbol)
|
252 |
-
|
253 |
return info_text, facts_text, fig
|
254 |
|
255 |
def handle_button_click(z: int):
|
@@ -261,14 +227,12 @@ def search_element(query: str):
|
|
261 |
return gr.update(), gr.update(), gr.update()
|
262 |
return element_info(query)
|
263 |
|
264 |
-
#
|
265 |
-
# UI (Gradio 4.29.0 compatible)
|
266 |
-
# -----------------------------
|
267 |
with gr.Blocks(title="Interactive Periodic Table") as demo:
|
268 |
-
gr.Markdown("
|
269 |
|
270 |
with gr.Row():
|
271 |
-
# Inspector
|
272 |
with gr.Column(scale=1):
|
273 |
gr.Markdown("### Inspector")
|
274 |
search = gr.Textbox(label="Search (symbol/name/Z)", placeholder="e.g., C, Iron, 79")
|
@@ -283,16 +247,15 @@ with gr.Blocks(title="Interactive Periodic Table") as demo:
|
|
283 |
prop.change(lambda k: plot_heatmap(k), inputs=[prop], outputs=[heat])
|
284 |
demo.load(lambda: plot_heatmap("electronegativity"), outputs=[heat])
|
285 |
|
|
|
286 |
with gr.Column(scale=2):
|
287 |
gr.Markdown("### Main Table")
|
288 |
-
# Group headers (1..18)
|
289 |
with gr.Row():
|
290 |
for g in range(1, 19):
|
291 |
gr.Markdown(f"**{g}**")
|
292 |
-
|
293 |
-
for r in range(MAX_PERIOD):
|
294 |
with gr.Row():
|
295 |
-
for c in range(
|
296 |
z = GRID[r][c]
|
297 |
if z is None:
|
298 |
gr.Button("", interactive=False)
|
@@ -306,15 +269,13 @@ with gr.Blocks(title="Interactive Periodic Table") as demo:
|
|
306 |
with gr.Row():
|
307 |
for z in LAN:
|
308 |
sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
|
309 |
-
|
310 |
-
|
311 |
-
outputs=[info, facts, trend])
|
312 |
with gr.Row():
|
313 |
for z in ACT:
|
314 |
sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
|
315 |
-
|
316 |
-
|
317 |
-
outputs=[info, facts, trend])
|
318 |
|
319 |
if __name__ == "__main__":
|
320 |
demo.launch()
|
|
|
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import pandas as pd
|
3 |
import numpy as np
|
4 |
import matplotlib.pyplot as plt
|
5 |
+
from typing import Dict, List, Optional
|
6 |
from periodictable import elements
|
7 |
|
8 |
+
# ---------- helpers ----------
|
|
|
|
|
9 |
def to_float(x):
|
|
|
10 |
if x is None:
|
11 |
return np.nan
|
12 |
+
v = getattr(x, "nominal_value", x) # handles uncertainties.UFloat
|
13 |
try:
|
|
|
|
|
14 |
return float(v)
|
15 |
except Exception:
|
16 |
+
return np.nan
|
|
|
|
|
|
|
17 |
|
|
|
|
|
|
|
18 |
NUMERIC_PROPS = [
|
19 |
("mass", "Atomic mass (u)"),
|
20 |
("density", "Density (g/cm^3)"),
|
|
|
26 |
]
|
27 |
|
28 |
CURATED_FACTS: Dict[str, List[str]] = {
|
29 |
+
"H": ["Lightest element; ~74% of visible matter is H in stars."],
|
30 |
+
"He": ["Inert and super light; cryogenics & balloons."],
|
31 |
+
"Li": ["Lithium-ion batteries power phones & EVs."],
|
32 |
+
"C": ["Diamond vs graphite = same element, different structure."],
|
33 |
+
"N": ["~78% of Earth's atmosphere is N₂."],
|
34 |
+
"O": ["~21% of air; essential for respiration."],
|
35 |
+
"Na": ["Reacts violently with water."],
|
36 |
+
"Mg": ["Bright white flame in flares."],
|
37 |
+
"Si": ["Semiconductor backbone."],
|
38 |
+
"Cl": ["Disinfectant; elemental Cl₂ is toxic."],
|
39 |
+
"Fe": ["Steel core; oxygen transport in blood (heme)."],
|
40 |
+
"Cu": ["Great conductor; forms green patina."],
|
41 |
+
"Ag": ["Highest electrical conductivity."],
|
42 |
+
"Au": ["Very unreactive; great for electronics/jewelry."],
|
43 |
+
"Hg": ["Liquid metal at room temp; toxic."],
|
44 |
+
"Pb": ["Dense, malleable; toxic—phase-out in fuels/paints."],
|
45 |
+
"U": ["Reactor fuel (U-235)."],
|
46 |
+
"Pu": ["Man-made in quantity; nuclear uses."],
|
47 |
+
"F": ["Most electronegative; extremely reactive."],
|
48 |
+
"Ne": ["Classic red-orange neon glow."],
|
49 |
+
"Xe": ["Used in bright flashes/HID lamps."],
|
|
|
|
|
50 |
}
|
51 |
|
52 |
GROUP_FACTS = {
|
53 |
+
"alkali": "Alkali metal: very reactive; forms +1 cations; reacts with water.",
|
54 |
+
"alkaline-earth": "Alkaline earth metal: reactive; forms +2 cations.",
|
55 |
+
"transition": "Transition metal: catalysts, colorful compounds, multiple oxidation states.",
|
56 |
+
"post-transition": "Post-transition metal: softer, lower melting than transition metals.",
|
57 |
+
"metalloid": "Metalloid: between metals and nonmetals; often semiconductors.",
|
58 |
+
"nonmetal": "Nonmetal: forms covalent compounds; huge biological roles.",
|
59 |
+
"halogen": "Halogen: very reactive nonmetals; −1 state; forms salts.",
|
60 |
+
"noble-gas": "Noble gas: inert, monatomic gases.",
|
61 |
+
"lanthanide": "Lanthanide: rare earths; magnets, lasers, phosphors.",
|
62 |
+
"actinide": "Actinide: radioactive; nuclear materials.",
|
63 |
}
|
64 |
|
65 |
def classify_category(el) -> str:
|
|
|
68 |
return "alkali"
|
69 |
if el.block == "s" and el.group == 2:
|
70 |
return "alkaline-earth"
|
71 |
+
if el.block == "d":
|
72 |
+
return "transition"
|
73 |
if el.block == "p" and el.group == 17:
|
74 |
return "halogen"
|
75 |
if el.block == "p" and el.group == 18:
|
76 |
return "noble-gas"
|
|
|
|
|
77 |
if el.block == "f" and 57 <= el.number <= 71:
|
78 |
return "lanthanide"
|
79 |
if el.block == "f" and 89 <= el.number <= 103:
|
|
|
92 |
el = elements[Z]
|
93 |
if el is None:
|
94 |
continue
|
95 |
+
rows.append({
|
96 |
"Z": el.number,
|
97 |
"symbol": el.symbol,
|
98 |
"name": el.name.title(),
|
99 |
"period": getattr(el, "period", None),
|
100 |
+
"group": getattr(el, "group", None),
|
101 |
"block": getattr(el, "block", None),
|
102 |
"mass": to_float(getattr(el, "mass", None)),
|
103 |
"density": to_float(getattr(el, "density", None)),
|
|
|
108 |
"covalent_radius": to_float(getattr(el, "covalent_radius", None)),
|
109 |
"category": classify_category(el),
|
110 |
"is_radioactive": bool(getattr(el, "radioactive", False)),
|
111 |
+
})
|
|
|
112 |
return pd.DataFrame(rows).sort_values("Z").reset_index(drop=True)
|
113 |
|
114 |
DF = build_elements_df()
|
115 |
|
116 |
+
# ---------- hardcoded main-grid layout (periods 1–7, groups 1–18) ----------
|
117 |
+
# None = empty cell; numbers = atomic numbers
|
118 |
+
GRID = [
|
119 |
+
# P1
|
120 |
+
[1, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 2],
|
121 |
+
# P2
|
122 |
+
[3, 4, None, None, None, None, None, None, None, None, None, None, 5, 6, 7, 8, 9, 10],
|
123 |
+
# P3
|
124 |
+
[11, 12, None, None, None, None, None, None, None, None, None, None, 13, 14, 15, 16, 17, 18],
|
125 |
+
# P4
|
126 |
+
[19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36],
|
127 |
+
# P5
|
128 |
+
[37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54],
|
129 |
+
# P6 (La shown at group 3)
|
130 |
+
[55, 56, 57, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86],
|
131 |
+
# P7 (Ac shown at group 3)
|
132 |
+
[87, 88, 89, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118],
|
133 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
+
# f-block lists we display separately (omit La & Ac because they’re in the main grid)
|
136 |
+
LAN = list(range(58, 72)) # Ce..Lu
|
137 |
+
ACT = list(range(90, 104)) # Th..Lr
|
138 |
|
139 |
+
# ---------- plotting ----------
|
|
|
|
|
140 |
def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
|
141 |
fig, ax = plt.subplots()
|
142 |
ax.scatter(trend_df["Z"], trend_df[prop_key])
|
|
|
152 |
|
153 |
def plot_heatmap(property_key: str):
|
154 |
prop_label = dict(NUMERIC_PROPS)[property_key]
|
155 |
+
max_period, max_group = len(GRID), len(GRID[0])
|
156 |
+
grid_vals = np.full((max_period, max_group), np.nan, dtype=float)
|
157 |
+
for r in range(max_period):
|
158 |
+
for c in range(max_group):
|
159 |
z = GRID[r][c]
|
160 |
if z is None:
|
161 |
continue
|
|
|
164 |
grid_vals[r, c] = float(val)
|
165 |
fig, ax = plt.subplots()
|
166 |
im = ax.imshow(grid_vals, origin="upper", aspect="auto")
|
167 |
+
ax.set_xticks(range(max_group))
|
168 |
+
ax.set_xticklabels([str(i) for i in range(1, max_group + 1)])
|
169 |
+
ax.set_yticks(range(max_period))
|
170 |
+
ax.set_yticklabels([str(i) for i in range(1, max_period + 1)])
|
171 |
ax.set_xlabel("Group")
|
172 |
ax.set_ylabel("Period")
|
173 |
ax.set_title(f"Periodic heatmap: {prop_label}")
|
|
|
175 |
fig.tight_layout()
|
176 |
return fig
|
177 |
|
178 |
+
# ---------- callbacks ----------
|
|
|
|
|
179 |
def element_info(z_or_symbol: str):
|
180 |
try:
|
181 |
if z_or_symbol.isdigit():
|
|
|
195 |
facts.append(GROUP_FACTS.get(row["category"], None))
|
196 |
facts = [f for f in facts if f]
|
197 |
|
198 |
+
def show(v): # nicer NaN -> —
|
199 |
+
return v if (v is not None and not pd.isna(v)) else "—"
|
200 |
+
|
201 |
props_lines = [
|
202 |
f"{row['name']} ({symbol}), Z = {Z}",
|
203 |
f"Period {int(row['period']) if not pd.isna(row['period']) else '—'}, "
|
204 |
f"Group {row['group'] if row['group'] is not None else '—'}, "
|
205 |
f"Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
|
206 |
+
f"Atomic mass: {show(row['mass'])} u",
|
207 |
+
f"Density: {show(row['density'])} g/cm³",
|
208 |
+
f"Electronegativity: {show(row['electronegativity'])} (Pauling)",
|
209 |
+
f"Melting point: {show(row['melting_point'])} K | Boiling point: {show(row['boiling_point'])} K",
|
210 |
+
f"vdW radius: {show(row['vdw_radius'])} pm | Covalent radius: {show(row['covalent_radius'])} pm",
|
|
|
|
|
211 |
f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
|
212 |
]
|
213 |
info_text = "\n".join(props_lines)
|
|
|
216 |
prop_key = "electronegativity" if not pd.isna(row["electronegativity"]) else "mass"
|
217 |
trend_df = DF[["Z", "symbol", prop_key]].dropna()
|
218 |
fig = plot_trend(trend_df, prop_key, Z, symbol)
|
|
|
219 |
return info_text, facts_text, fig
|
220 |
|
221 |
def handle_button_click(z: int):
|
|
|
227 |
return gr.update(), gr.update(), gr.update()
|
228 |
return element_info(query)
|
229 |
|
230 |
+
# ---------- UI ----------
|
|
|
|
|
231 |
with gr.Blocks(title="Interactive Periodic Table") as demo:
|
232 |
+
gr.Markdown("Click an element or search by symbol/name/atomic number.")
|
233 |
|
234 |
with gr.Row():
|
235 |
+
# Inspector
|
236 |
with gr.Column(scale=1):
|
237 |
gr.Markdown("### Inspector")
|
238 |
search = gr.Textbox(label="Search (symbol/name/Z)", placeholder="e.g., C, Iron, 79")
|
|
|
247 |
prop.change(lambda k: plot_heatmap(k), inputs=[prop], outputs=[heat])
|
248 |
demo.load(lambda: plot_heatmap("electronegativity"), outputs=[heat])
|
249 |
|
250 |
+
# Main table
|
251 |
with gr.Column(scale=2):
|
252 |
gr.Markdown("### Main Table")
|
|
|
253 |
with gr.Row():
|
254 |
for g in range(1, 19):
|
255 |
gr.Markdown(f"**{g}**")
|
256 |
+
for r in range(len(GRID)):
|
|
|
257 |
with gr.Row():
|
258 |
+
for c in range(len(GRID[0])):
|
259 |
z = GRID[r][c]
|
260 |
if z is None:
|
261 |
gr.Button("", interactive=False)
|
|
|
269 |
with gr.Row():
|
270 |
for z in LAN:
|
271 |
sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
|
272 |
+
gr.Button(sym).click(handle_button_click, inputs=[gr.Number(z, visible=False)],
|
273 |
+
outputs=[info, facts, trend])
|
|
|
274 |
with gr.Row():
|
275 |
for z in ACT:
|
276 |
sym = DF.loc[DF["Z"] == z, "symbol"].values[0]
|
277 |
+
gr.Button(sym).click(handle_button_click, inputs=[gr.Number(z, visible=False)],
|
278 |
+
outputs=[info, facts, trend])
|
|
|
279 |
|
280 |
if __name__ == "__main__":
|
281 |
demo.launch()
|