mic3333 commited on
Commit
b4d9d47
Β·
verified Β·
1 Parent(s): 0f968e0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -334
app.py CHANGED
@@ -1,22 +1,17 @@
1
  #!/usr/bin/env python3
2
  """
3
- Plotly Express Integration Fix - Address the Root Cause
4
  """
5
 
6
  import gradio as gr
7
 
8
- def create_pyodide_interface():
9
- """Fix Express by converting it to Graph Objects under the hood"""
10
-
11
- pyodide_html = '''
12
- <div id="pyodide-container" style="border: 1px solid #ddd; padding: 15px; border-radius: 5px; margin: 10px 0;">
13
- <div id="status" style="font-weight: bold; padding: 10px; background: #f0f0f0; border-radius: 3px;">
14
- πŸ”„ Loading with Express fix...
15
- </div>
16
- <div id="output" style="display:none; margin-top: 10px;">
17
- <h4>Output:</h4>
18
- <pre id="text-output" style="background: #f8f8f8; padding: 10px; border-radius: 3px; max-height: 200px; overflow-y: auto;"></pre>
19
- <div id="plots" style="margin-top: 15px;"></div>
20
  </div>
21
  </div>
22
 
@@ -24,364 +19,121 @@ def create_pyodide_interface():
24
  <script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js"></script>
25
 
26
  <script>
27
- let pyodide = null;
28
- let ready = false;
29
- let plotCount = 0;
30
 
31
- function updateStatus(msg, color = 'black') {
32
  document.getElementById('status').innerHTML = msg;
33
  document.getElementById('status').style.color = color;
34
  }
35
 
36
- window.renderPlot = function(data, layout) {
37
- try {
38
- plotCount++;
39
- const plotId = 'plot-' + plotCount;
40
- const plotsDiv = document.getElementById('plots');
41
-
42
- const plotDiv = document.createElement('div');
43
- plotDiv.innerHTML = `
44
- <h5>πŸ“Š Plot ${plotCount}</h5>
45
- <div id="${plotId}" style="width: 100%; height: 450px; border: 1px solid #ccc; margin: 10px 0;"></div>
46
- `;
47
- plotsDiv.appendChild(plotDiv);
48
-
49
- if (typeof data === 'string') data = JSON.parse(data);
50
- if (typeof layout === 'string') layout = JSON.parse(layout);
51
-
52
- Plotly.newPlot(plotId, data, layout, {responsive: true});
53
- console.log('Plot rendered:', plotId);
54
- return true;
55
-
56
- } catch (error) {
57
- console.error('Plot error:', error);
58
- return false;
59
- }
60
  };
61
 
62
  async function init() {
63
- try {
64
- updateStatus('πŸ”„ Loading Pyodide...', 'blue');
65
- pyodide = await loadPyodide();
66
-
67
- updateStatus('πŸ“¦ Installing packages...', 'blue');
68
- await pyodide.loadPackage(['numpy', 'pandas', 'micropip']);
69
-
70
- updateStatus('πŸ“ˆ Installing Plotly...', 'blue');
71
- await pyodide.runPythonAsync(`
72
- import micropip
73
- await micropip.install('plotly')
74
- `);
75
-
76
- updateStatus('πŸ”§ Fixing Express integration...', 'blue');
77
-
78
- // The key fix: properly handle Express vs Graph Objects
79
- pyodide.runPython(`
80
- import json
81
- import numpy as np
82
- from js import renderPlot
83
-
84
- def convert_numpy_to_lists(obj):
85
- """Recursively convert numpy arrays to Python lists"""
86
- if isinstance(obj, np.ndarray):
87
- return obj.tolist()
88
- elif isinstance(obj, dict):
89
- return {k: convert_numpy_to_lists(v) for k, v in obj.items()}
90
- elif isinstance(obj, list):
91
- return [convert_numpy_to_lists(item) for item in obj]
92
- else:
93
- return obj
94
-
95
- def show_any_figure(fig):
96
- """Universal show function that works for both Express and Graph Objects"""
97
- try:
98
- print(f"Rendering figure of type: {type(fig)}")
99
-
100
- # Convert figure to dictionary
101
- fig_dict = fig.to_dict()
102
-
103
- print(f"Figure has {len(fig_dict.get('data', []))} traces")
104
-
105
- # Process each trace
106
- fixed_data = []
107
- for i, trace in enumerate(fig_dict.get('data', [])):
108
- print(f"Processing trace {i}: type={trace.get('type', 'unknown')}")
109
 
110
- # Create a copy of the trace
111
- fixed_trace = dict(trace)
112
-
113
- # Fix x data
114
- if 'x' in fixed_trace:
115
- x_data = fixed_trace['x']
116
- if isinstance(x_data, dict) and 'bdata' in x_data:
117
- # NumPy binary data - convert from original figure
118
- original_trace = fig.data[i]
119
- if hasattr(original_trace, 'x') and hasattr(original_trace.x, 'tolist'):
120
- fixed_trace['x'] = original_trace.x.tolist()
121
- print(f" Converted x from NumPy: {fixed_trace['x']}")
122
- else:
123
- # Regular data - ensure it's a list
124
- fixed_trace['x'] = convert_numpy_to_lists(x_data)
125
- print(f" X data: {fixed_trace['x']}")
126
-
127
- # Fix y data
128
- if 'y' in fixed_trace:
129
- y_data = fixed_trace['y']
130
- if isinstance(y_data, dict) and 'bdata' in y_data:
131
- # NumPy binary data
132
- original_trace = fig.data[i]
133
- if hasattr(original_trace, 'y') and hasattr(original_trace.y, 'tolist'):
134
- fixed_trace['y'] = original_trace.y.tolist()
135
- print(f" Converted y from NumPy: {fixed_trace['y']}")
136
- else:
137
- # Regular data
138
- fixed_trace['y'] = convert_numpy_to_lists(y_data)
139
- print(f" Y data: {fixed_trace['y']}")
140
-
141
- fixed_data.append(fixed_trace)
142
-
143
- # Create the plot data
144
- plot_data = json.dumps(fixed_data)
145
- plot_layout = json.dumps(fig_dict.get('layout', {}))
146
-
147
- print(f"Sending to JavaScript - data length: {len(plot_data)}")
148
-
149
- # Render the plot
150
- success = renderPlot(plot_data, plot_layout)
151
-
152
- if success:
153
- print("βœ… Plot rendered successfully!")
154
- else:
155
- print("❌ Plot rendering failed")
156
-
157
- return success
158
-
159
- except Exception as e:
160
- print(f"❌ Figure rendering error: {e}")
161
- import traceback
162
- traceback.print_exc()
163
- return False
164
 
165
- # Setup both Graph Objects and Express
166
- try:
167
- import plotly.graph_objects as go
168
- import plotly.express as px
169
- import pandas as pd
170
-
171
- # Replace the show method for Graph Objects
172
- go.Figure.show = lambda self, *args, **kwargs: show_any_figure(self)
173
-
174
- # The key insight: Express functions return Figure objects
175
- # So we don't need to patch px functions individually
176
- # Our go.Figure.show patch will handle Express figures too!
177
-
178
- print("βœ… Both Graph Objects and Express patched!")
179
- print("βœ… Universal figure rendering enabled!")
180
-
181
- except Exception as e:
182
- print(f"❌ Setup error: {e}")
183
- import traceback
184
- traceback.print_exc()
185
-
186
- print("πŸŽ‰ Environment ready with Express fix!")
187
- `);
188
-
189
- ready = true;
190
- updateStatus('βœ… Ready with Express fix!', 'green');
191
-
192
- document.getElementById('output').style.display = 'block';
193
- document.getElementById('text-output').textContent = 'Express integration fixed! Both px and go should work now.';
194
-
195
- } catch (error) {
196
- console.error('Init error:', error);
197
- updateStatus('❌ Error: ' + error.message, 'red');
198
- }
199
  }
200
 
201
- async function runCode(code) {
202
  if (!ready) return 'Not ready';
203
 
204
- try {
205
- updateStatus('▢️ Running...', 'blue');
206
-
207
- document.getElementById('plots').innerHTML = '';
208
-
209
- pyodide.runPython(`
210
  import sys
211
  from io import StringIO
212
- old_stdout = sys.stdout
213
- sys.stdout = capture = StringIO()
214
- `);
215
-
216
- pyodide.runPython(code);
217
-
218
- let output = pyodide.runPython(`
219
- sys.stdout = old_stdout
220
- capture.getvalue()
221
- `);
222
-
223
- document.getElementById('text-output').textContent = output || 'Code executed';
224
- updateStatus('βœ… Complete', 'green');
225
-
226
- return output || 'Success';
227
-
228
- } catch (error) {
229
- const err = 'Error: ' + error.toString();
230
- document.getElementById('text-output').textContent = err;
231
- updateStatus('❌ Error', 'red');
232
- return err;
233
- }
234
  }
235
 
236
- function waitForReady() {
 
237
  if (typeof loadPyodide !== 'undefined' && typeof Plotly !== 'undefined') {
238
  init();
239
- } else {
240
- setTimeout(waitForReady, 1000);
241
  }
242
- }
243
-
244
- waitForReady();
245
-
246
- window.runCode = runCode;
247
 
 
248
  </script>
249
  '''
250
-
251
- return pyodide_html
252
 
253
  with gr.Blocks() as demo:
254
- gr.Markdown("# πŸ”§ Plotly Express Fix")
255
- gr.Markdown("**Universal fix** for both Graph Objects and Express data display issues")
256
 
257
- pyodide_interface = gr.HTML(create_pyodide_interface())
258
 
259
  with gr.Row():
260
- with gr.Column():
261
- code_input = gr.Textbox(
262
- value="""# Test the Express fix
263
- import plotly.express as px
264
  import plotly.graph_objects as go
265
- import pandas as pd
266
- import numpy as np
267
-
268
- print("=== Test 1: Express Scatter ===")
269
- df = pd.DataFrame({
270
- 'x': [1, 2, 3, 4, 5],
271
- 'y': [2, 4, 6, 8, 10]
272
- })
273
- fig1 = px.scatter(df, x='x', y='y', title='Express Scatter - Fixed!')
274
- fig1.show()
275
-
276
- print("=== Test 2: Express Line ===")
277
- fig2 = px.line(df, x='x', y='y', title='Express Line - Fixed!')
278
- fig2.show()
279
-
280
- print("=== Test 3: Graph Objects (should still work) ===")
281
- x = np.array([1, 2, 3, 4, 5])
282
- y = np.array([1, 4, 9, 16, 25])
283
-
284
- fig3 = go.Figure()
285
- fig3.add_trace(go.Scatter(x=x, y=y, mode='markers+lines', name='GO Test'))
286
- fig3.update_layout(title='Graph Objects - Should Still Work')
287
- fig3.show()
288
 
289
- print("βœ… All tests complete! Check if data points are visible.")""",
290
- lines=18,
291
- label="Test Code"
292
- )
293
-
294
- run_btn = gr.Button("πŸ§ͺ Test Fix", variant="primary", size="lg")
295
-
296
- with gr.Column():
297
- # Quick test buttons
298
- express_test_btn = gr.Button("πŸ“Š Quick Express Test")
299
- go_test_btn = gr.Button("πŸ“Š Quick GO Test")
300
- comparison_btn = gr.Button("πŸ“Š Side-by-Side Test")
301
-
302
- status_output = gr.Textbox(
303
- label="Result",
304
- interactive=False,
305
- lines=4
306
- )
307
-
308
- gr.Markdown("""
309
- ### πŸ”‘ **The Key Insight:**
310
-
311
- **Problem**: Plotly Express functions return `Figure` objects, but they have different internal data structures than Graph Objects figures.
312
-
313
- **Solution**: Create a **universal figure renderer** that handles both:
314
- - βœ… **Graph Objects**: Direct trace creation
315
- - βœ… **Express**: Converts Express figures using same logic
316
-
317
- ### 🎯 **What This Fix Does:**
318
-
319
- 1. **Universal Handler**: One function handles both `px.scatter()` and `go.Figure()`
320
- 2. **Smart Data Conversion**: Detects and converts NumPy arrays in both cases
321
- 3. **Type Detection**: Identifies figure source and processes accordingly
322
-
323
- ### πŸ§ͺ **Expected Results:**
324
-
325
- After this fix, **ALL** of these should show data points:
326
- - βœ… `px.scatter()`, `px.line()`, `px.bar()`
327
- - βœ… `go.Figure().add_trace()`
328
- - βœ… Both with DataFrames and NumPy arrays
329
- """)
330
-
331
- def get_express_test():
332
- return """# Quick Express test
333
  import plotly.express as px
334
  import pandas as pd
335
-
336
  df = pd.DataFrame({'x': [1,2,3,4], 'y': [1,4,9,16]})
337
- fig = px.scatter(df, x='x', y='y', title='Express Test')
338
- fig.show()
339
- print("Express test done!")"""
 
 
 
 
340
 
341
- def get_go_test():
342
- return """# Quick Graph Objects test
343
- import plotly.graph_objects as go
344
-
345
- fig = go.Figure()
346
- fig.add_trace(go.Scatter(x=[1,2,3,4], y=[1,4,9,16], mode='markers'))
347
- fig.update_layout(title='Graph Objects Test')
348
- fig.show()
349
- print("GO test done!")"""
350
 
351
- def get_comparison():
352
- return """# Side-by-side comparison
353
- import plotly.express as px
354
- import plotly.graph_objects as go
355
- import pandas as pd
356
-
357
- # Same data, different methods
358
- data_x = [1, 2, 3, 4, 5]
359
- data_y = [2, 4, 6, 8, 10]
360
-
361
- print("Creating Express version...")
362
- df = pd.DataFrame({'x': data_x, 'y': data_y})
363
- fig1 = px.scatter(df, x='x', y='y', title='Express Method')
364
- fig1.show()
365
-
366
- print("Creating Graph Objects version...")
367
- fig2 = go.Figure()
368
- fig2.add_trace(go.Scatter(x=data_x, y=data_y, mode='markers', name='GO Method'))
369
- fig2.update_layout(title='Graph Objects Method')
370
- fig2.show()
371
-
372
- print("Both should now show identical data points!")"""
373
-
374
- # Event handlers
375
- run_btn.click(
376
  fn=None,
377
- inputs=[code_input],
378
- outputs=[status_output],
379
- js="(code) => window.runCode ? window.runCode(code) : 'Not ready'"
380
  )
381
-
382
- express_test_btn.click(fn=get_express_test, outputs=[code_input])
383
- go_test_btn.click(fn=get_go_test, outputs=[code_input])
384
- comparison_btn.click(fn=get_comparison, outputs=[code_input])
385
 
386
  if __name__ == "__main__":
387
- demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
 
1
  #!/usr/bin/env python3
2
  """
3
+ Minimal Working Plotly - Simple and Clean
4
  """
5
 
6
  import gradio as gr
7
 
8
+ def create_interface():
9
+ html = '''
10
+ <div style="border: 1px solid #ddd; padding: 20px; border-radius: 5px;">
11
+ <div id="status">πŸ”„ Loading...</div>
12
+ <div id="output" style="display:none; margin-top: 20px;">
13
+ <pre id="text" style="background: #f5f5f5; padding: 10px; max-height: 150px; overflow-y: auto;"></pre>
14
+ <div id="plots"></div>
 
 
 
 
 
15
  </div>
16
  </div>
17
 
 
19
  <script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js"></script>
20
 
21
  <script>
22
+ let pyodide, ready = false, plotNum = 0;
 
 
23
 
24
+ function status(msg, color = 'black') {
25
  document.getElementById('status').innerHTML = msg;
26
  document.getElementById('status').style.color = color;
27
  }
28
 
29
+ window.makePlot = function(data, layout) {
30
+ const id = 'plot' + (++plotNum);
31
+ const div = document.createElement('div');
32
+ div.innerHTML = `<h4>Plot ${plotNum}</h4><div id="${id}" style="width:100%; height:400px; border:1px solid #ccc;"></div>`;
33
+ document.getElementById('plots').appendChild(div);
34
+
35
+ Plotly.newPlot(id, JSON.parse(data), JSON.parse(layout));
36
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  };
38
 
39
  async function init() {
40
+ status('Loading...', 'blue');
41
+ pyodide = await loadPyodide();
42
+ await pyodide.loadPackage(['numpy', 'pandas', 'micropip']);
43
+ await pyodide.runPythonAsync('import micropip; await micropip.install("plotly")');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ pyodide.runPython(`
46
+ import json, numpy as np
47
+ from js import makePlot
48
+
49
+ def show(fig):
50
+ d = fig.to_dict()
51
+ # Convert numpy arrays to lists
52
+ for trace in d.get('data', []):
53
+ if 'x' in trace and hasattr(trace['x'], 'tolist'):
54
+ trace['x'] = trace['x'].tolist()
55
+ if 'y' in trace and hasattr(trace['y'], 'tolist'):
56
+ trace['y'] = trace['y'].tolist()
57
+
58
+ makePlot(json.dumps(d['data']), json.dumps(d.get('layout', {})))
59
+ print("Plot created!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
+ import plotly.graph_objects as go
62
+ import plotly.express as px
63
+ go.Figure.show = lambda self: show(self)
64
+ `);
65
+
66
+ ready = true;
67
+ status('βœ… Ready!', 'green');
68
+ document.getElementById('output').style.display = 'block';
69
+ document.getElementById('text').textContent = 'Ready to plot!';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
+ async function run(code) {
73
  if (!ready) return 'Not ready';
74
 
75
+ document.getElementById('plots').innerHTML = '';
76
+
77
+ pyodide.runPython(`
 
 
 
78
  import sys
79
  from io import StringIO
80
+ old = sys.stdout
81
+ sys.stdout = cap = StringIO()
82
+ `);
83
+
84
+ pyodide.runPython(code);
85
+
86
+ const output = pyodide.runPython('sys.stdout = old; cap.getvalue()');
87
+ document.getElementById('text').textContent = output || 'Done';
88
+
89
+ return output || 'Success';
 
 
 
 
 
 
 
 
 
 
 
 
90
  }
91
 
92
+ // Start
93
+ setTimeout(() => {
94
  if (typeof loadPyodide !== 'undefined' && typeof Plotly !== 'undefined') {
95
  init();
 
 
96
  }
97
+ }, 1000);
 
 
 
 
98
 
99
+ window.run = run;
100
  </script>
101
  '''
102
+ return html
 
103
 
104
  with gr.Blocks() as demo:
105
+ gr.Markdown("# πŸ“Š Simple Plotly")
 
106
 
107
+ interface = gr.HTML(create_interface())
108
 
109
  with gr.Row():
110
+ code = gr.Textbox(
111
+ value="""# Test 1: Graph Objects
 
 
112
  import plotly.graph_objects as go
113
+ fig = go.Figure()
114
+ fig.add_trace(go.Scatter(x=[1,2,3,4], y=[1,4,9,16], mode='markers+lines'))
115
+ fig.show()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
+ # Test 2: Express
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  import plotly.express as px
119
  import pandas as pd
 
120
  df = pd.DataFrame({'x': [1,2,3,4], 'y': [1,4,9,16]})
121
+ fig = px.scatter(df, x='x', y='y')
122
+ fig.show()""",
123
+ lines=10,
124
+ label="Code"
125
+ )
126
+
127
+ result = gr.Textbox(label="Result", lines=2, interactive=False)
128
 
129
+ btn = gr.Button("▢️ Run")
 
 
 
 
 
 
 
 
130
 
131
+ btn.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  fn=None,
133
+ inputs=[code],
134
+ outputs=[result],
135
+ js="(code) => window.run ? window.run(code) : 'Loading...'"
136
  )
 
 
 
 
137
 
138
  if __name__ == "__main__":
139
+ demo.launch(server_name="0.0.0.0", server_port=7860)