ex4nicegui 0.6.6__py3-none-any.whl → 0.6.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ex4nicegui/__init__.py +10 -6
- ex4nicegui/gsap/__init__.py +11 -0
- ex4nicegui/helper/__init__.py +4 -0
- ex4nicegui/helper/client_instance_locker.py +31 -0
- ex4nicegui/libs/gsap/.DS_Store +0 -0
- ex4nicegui/libs/gsap/gsap.mjs +6 -0
- ex4nicegui/reactive/EChartsComponent/ECharts.js +19 -9
- ex4nicegui/reactive/local_file_picker.py +1 -2
- ex4nicegui/reactive/officials/aggrid.py +7 -7
- ex4nicegui/reactive/officials/base.py +198 -86
- ex4nicegui/reactive/officials/button.py +5 -5
- ex4nicegui/reactive/officials/checkbox.py +5 -6
- ex4nicegui/reactive/officials/circular_progress.py +5 -6
- ex4nicegui/reactive/officials/color_picker.py +7 -8
- ex4nicegui/reactive/officials/column.py +4 -3
- ex4nicegui/reactive/officials/date.py +6 -12
- ex4nicegui/reactive/officials/drawer.py +2 -3
- ex4nicegui/reactive/officials/echarts.py +32 -15
- ex4nicegui/reactive/officials/expansion.py +5 -5
- ex4nicegui/reactive/officials/grid.py +1 -1
- ex4nicegui/reactive/officials/html.py +1 -3
- ex4nicegui/reactive/officials/icon.py +9 -10
- ex4nicegui/reactive/officials/image.py +5 -7
- ex4nicegui/reactive/officials/input.py +8 -10
- ex4nicegui/reactive/officials/knob.py +5 -7
- ex4nicegui/reactive/officials/label.py +5 -4
- ex4nicegui/reactive/officials/linear_progress.py +9 -10
- ex4nicegui/reactive/officials/number.py +23 -8
- ex4nicegui/reactive/officials/radio.py +7 -9
- ex4nicegui/reactive/officials/row.py +1 -1
- ex4nicegui/reactive/officials/select.py +7 -9
- ex4nicegui/reactive/officials/slider.py +6 -8
- ex4nicegui/reactive/officials/switch.py +5 -7
- ex4nicegui/reactive/officials/tab.py +1 -1
- ex4nicegui/reactive/officials/tab_panel.py +1 -1
- ex4nicegui/reactive/officials/tab_panels.py +15 -3
- ex4nicegui/reactive/officials/table.py +12 -10
- ex4nicegui/reactive/officials/tabs.py +4 -3
- ex4nicegui/reactive/officials/textarea.py +6 -8
- ex4nicegui/reactive/officials/upload.py +2 -2
- ex4nicegui/reactive/q_pagination.py +5 -4
- ex4nicegui/reactive/scopedStyle.js +55 -0
- ex4nicegui/reactive/scopedStyle.py +22 -0
- ex4nicegui/reactive/services/color_service.py +56 -0
- ex4nicegui/reactive/services/pandas_service.py +31 -0
- ex4nicegui/reactive/{utils.py → services/reactive_service.py} +9 -67
- ex4nicegui/reactive/systems/color_system.py +25 -0
- ex4nicegui/reactive/systems/object_system.py +33 -0
- ex4nicegui/reactive/systems/reactive_system.py +21 -0
- ex4nicegui/reactive/useMouse/UseMouse.py +4 -4
- ex4nicegui/reactive/usePagination.py +1 -1
- ex4nicegui/reactive/vfor.py +1 -2
- ex4nicegui/reactive/vmodel.py +1 -1
- ex4nicegui/utils/refComputed.py +147 -0
- ex4nicegui/utils/refWrapper.py +57 -0
- ex4nicegui/utils/signals.py +51 -192
- ex4nicegui/utils/types.py +16 -0
- ex4nicegui/version.py +3 -0
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/METADATA +1274 -1124
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/RECORD +81 -74
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/WHEEL +1 -2
- ex4nicegui/reactive/EChartsComponent/__init__.py +0 -0
- ex4nicegui/reactive/UseDraggable/__init__.py +0 -0
- ex4nicegui/reactive/dropZone/__init__.py +0 -0
- ex4nicegui/reactive/mermaid/__init__.py +0 -0
- ex4nicegui/reactive/officials/__init__.py +0 -1
- ex4nicegui/reactive/officials/utils.py +0 -11
- ex4nicegui/reactive/useMouse/__init__.py +0 -0
- ex4nicegui-0.6.6.dist-info/top_level.txt +0 -1
- {ex4nicegui-0.6.6.dist-info → ex4nicegui-0.6.8.dist-info}/LICENSE +0 -0
|
@@ -1,1124 +1,1274 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: ex4nicegui
|
|
3
|
-
Version: 0.6.
|
|
4
|
-
Summary: Extension library based on nicegui, providing data responsive,BI functionality modules
|
|
5
|
-
Home-page:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
-
Classifier:
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Requires-Dist:
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
a
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
data = [1,
|
|
264
|
-
data_ref = deep_ref(data)
|
|
265
|
-
|
|
266
|
-
assert data_ref.value is not data
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
def
|
|
405
|
-
a2.value
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
rxui
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
rxui
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
#
|
|
479
|
-
rxui.input(value=
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
bg_color =
|
|
578
|
-
has_error =
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
},
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
ui.
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
ds.
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
)
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
```
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
```
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ex4nicegui
|
|
3
|
+
Version: 0.6.8
|
|
4
|
+
Summary: Extension library based on nicegui, providing data responsive,BI functionality modules
|
|
5
|
+
Home-page: https://github.com/CrystalWindSnake/ex4nicegui
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: nicegui,ex4nicegui,webui
|
|
8
|
+
Author: CrystalWindSnake
|
|
9
|
+
Author-email: 568166495@qq.com
|
|
10
|
+
Requires-Python: >=3.8,<4.0
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Dist: executing (>=2.0.1,<3.0.0)
|
|
19
|
+
Requires-Dist: nicegui (>=1.4.25,<2.0.0)
|
|
20
|
+
Requires-Dist: signe (>=0.4.14,<0.5.0)
|
|
21
|
+
Project-URL: Repository, https://github.com/CrystalWindSnake/ex4nicegui
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# ex4nicegui
|
|
25
|
+
[ENGLISH README](./README.en.md)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
- [教程](#教程)
|
|
29
|
+
- [安装](#-安装)
|
|
30
|
+
- [使用](#-使用)
|
|
31
|
+
- [功能](#-功能)
|
|
32
|
+
- [BI 模块](#bi-模块)
|
|
33
|
+
|
|
34
|
+
对 [nicegui](https://github.com/zauberzeug/nicegui) 做的扩展库。内置响应式组件,完全实现数据响应式界面编程。
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## 教程
|
|
38
|
+
[头条文章-秒杀官方实现,python界面库,去掉90%事件代码的nicegui](https://www.toutiao.com/item/7253786340574265860/)
|
|
39
|
+
|
|
40
|
+
[微信公众号-秒杀官方实现,python界面库,去掉90%事件代码的nicegui](https://mp.weixin.qq.com/s?__biz=MzUzNDk1MTc5Mw==&mid=2247486796&idx=1&sn=457ed6fb9d6a25145f7704d5197d670d&chksm=fa8daf52cdfa2644bede50ae7f2551162ecaedecafec231ee4ce8f28775a599f8669ecf06af1#rd)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## 📦 安装
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
pip install ex4nicegui -U
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## 示例项目
|
|
51
|
+
- [入门](./examples/basic/)
|
|
52
|
+
- [todo list mvc](./examples/todomvc/)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🦄 使用
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from nicegui import ui
|
|
60
|
+
from ex4nicegui import ref_computed, effect, to_ref
|
|
61
|
+
from ex4nicegui.reactive import rxui
|
|
62
|
+
|
|
63
|
+
# 定义响应式数据
|
|
64
|
+
r_input = to_ref("")
|
|
65
|
+
|
|
66
|
+
# 按照 nicegui 使用方式传入响应式数据即可
|
|
67
|
+
rxui.input(value=r_input)
|
|
68
|
+
rxui.label(r_input)
|
|
69
|
+
|
|
70
|
+
ui.run()
|
|
71
|
+
```
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
### 提供 echarts 图表组件
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from nicegui import ui
|
|
79
|
+
from ex4nicegui import ref_computed, effect, to_ref
|
|
80
|
+
from ex4nicegui.reactive import rxui
|
|
81
|
+
|
|
82
|
+
r_input = to_ref("")
|
|
83
|
+
|
|
84
|
+
# ref_computed 创建只读响应式变量
|
|
85
|
+
# 函数中使用任意其他响应式变量,会自动关联
|
|
86
|
+
@ref_computed
|
|
87
|
+
def cp_echarts_opts():
|
|
88
|
+
return {
|
|
89
|
+
"title": {"text": r_input.value}, #字典中使用任意响应式变量,通过 .value 获取值
|
|
90
|
+
"xAxis": {
|
|
91
|
+
"type": "category",
|
|
92
|
+
"data": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
|
93
|
+
},
|
|
94
|
+
"yAxis": {"type": "value"},
|
|
95
|
+
"series": [
|
|
96
|
+
{
|
|
97
|
+
"data": [120, 200, 150, 80, 70, 110, 130],
|
|
98
|
+
"type": "bar",
|
|
99
|
+
"showBackground": True,
|
|
100
|
+
"backgroundStyle": {"color": "rgba(180, 180, 180, 0.2)"},
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
input = rxui.input("输入内容,图表标题会同步", value=r_input)
|
|
106
|
+
# 通过响应式组件对象的 element 属性,获取原生 nicegui 组件对象
|
|
107
|
+
input.element.classes("w-full")
|
|
108
|
+
|
|
109
|
+
rxui.echarts(cp_echarts_opts)
|
|
110
|
+
|
|
111
|
+
ui.run()
|
|
112
|
+
```
|
|
113
|
+

|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
### echarts 图表鼠标事件
|
|
117
|
+
|
|
118
|
+
`on` 函数参数 `event_name` 以及 `query` 使用,查看[echarts 事件中文文档](https://echarts.apache.org/handbook/zh/concepts/event/)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
以下例子绑定鼠标单击事件
|
|
122
|
+
```python
|
|
123
|
+
from nicegui import ui
|
|
124
|
+
from ex4nicegui.reactive import rxui
|
|
125
|
+
|
|
126
|
+
opts = {
|
|
127
|
+
"xAxis": {"type": "value", "boundaryGap": [0, 0.01]},
|
|
128
|
+
"yAxis": {
|
|
129
|
+
"type": "category",
|
|
130
|
+
"data": ["Brazil", "Indonesia", "USA", "India", "China", "World"],
|
|
131
|
+
},
|
|
132
|
+
"series": [
|
|
133
|
+
{
|
|
134
|
+
"name": "first",
|
|
135
|
+
"type": "bar",
|
|
136
|
+
"data": [18203, 23489, 29034, 104970, 131744, 630230],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"name": "second",
|
|
140
|
+
"type": "bar",
|
|
141
|
+
"data": [19325, 23438, 31000, 121594, 134141, 681807],
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
bar = rxui.echarts(opts)
|
|
147
|
+
|
|
148
|
+
def on_click(e: rxui.echarts.EChartsMouseEventArguments):
|
|
149
|
+
ui.notify(f"on_click:{e.seriesName}:{e.name}:{e.value}")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
bar.on("click", on_click)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
以下例子只针对指定系列触发鼠标划过事件
|
|
157
|
+
```python
|
|
158
|
+
from nicegui import ui
|
|
159
|
+
from ex4nicegui.reactive import rxui
|
|
160
|
+
|
|
161
|
+
opts = {
|
|
162
|
+
"xAxis": {"type": "value", "boundaryGap": [0, 0.01]},
|
|
163
|
+
"yAxis": {
|
|
164
|
+
"type": "category",
|
|
165
|
+
"data": ["Brazil", "Indonesia", "USA", "India", "China", "World"],
|
|
166
|
+
},
|
|
167
|
+
"series": [
|
|
168
|
+
{
|
|
169
|
+
"name": "first",
|
|
170
|
+
"type": "bar",
|
|
171
|
+
"data": [18203, 23489, 29034, 104970, 131744, 630230],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "second",
|
|
175
|
+
"type": "bar",
|
|
176
|
+
"data": [19325, 23438, 31000, 121594, 134141, 681807],
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
bar = rxui.echarts(opts)
|
|
182
|
+
|
|
183
|
+
def on_first_series_mouseover(e: rxui.echarts.EChartsMouseEventArguments):
|
|
184
|
+
ui.notify(f"on_first_series_mouseover:{e.seriesName}:{e.name}:{e.value}")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
bar.on("mouseover", on_first_series_mouseover, query={"seriesName": "first"})
|
|
188
|
+
|
|
189
|
+
ui.run()
|
|
190
|
+
```
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
## 响应式
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from ex4nicegui import (
|
|
198
|
+
to_ref,
|
|
199
|
+
ref_computed,
|
|
200
|
+
on,
|
|
201
|
+
effect,
|
|
202
|
+
effect_refreshable,
|
|
203
|
+
batch,
|
|
204
|
+
event_batch,
|
|
205
|
+
deep_ref,
|
|
206
|
+
async_computed
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
常用 `to_ref`,`deep_ref`,`effect`,`ref_computed`,`on`,`async_computed`
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `to_ref`
|
|
214
|
+
定义响应式对象,通过 `.value` 读写
|
|
215
|
+
```python
|
|
216
|
+
a = to_ref(1)
|
|
217
|
+
b = to_ref("text")
|
|
218
|
+
|
|
219
|
+
a.value =2
|
|
220
|
+
b.value = 'new text'
|
|
221
|
+
|
|
222
|
+
print(a.value)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
当值为复杂对象时,默认不会保持嵌套对象的响应性。
|
|
226
|
+
```python
|
|
227
|
+
a = to_ref([1,2])
|
|
228
|
+
|
|
229
|
+
@effect
|
|
230
|
+
def _():
|
|
231
|
+
print('len:',len(a.value))
|
|
232
|
+
|
|
233
|
+
# 不会触发 effect
|
|
234
|
+
a.value.append(10)
|
|
235
|
+
|
|
236
|
+
# 整个替换则会触发
|
|
237
|
+
a.value = [1,2,10]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
参数 `is_deep` 设置为 `True` 时,能得到深度响应能力
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
a = to_ref([1,2],is_deep=True)
|
|
244
|
+
|
|
245
|
+
@effect
|
|
246
|
+
def _():
|
|
247
|
+
print('len:',len(a.value))
|
|
248
|
+
|
|
249
|
+
# print 3
|
|
250
|
+
a.value.append(10)
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
> `deep_ref` 等价于 `is_deep` 设置为 `True` 时的 `to_ref`
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### `deep_ref`
|
|
259
|
+
等价于 `is_deep` 设置为 `True` 时的 `to_ref`。
|
|
260
|
+
|
|
261
|
+
当数据源为列表、字典或自定义类时,特别有用。通过 `.value` 获取的对象为代理对象
|
|
262
|
+
```python
|
|
263
|
+
data = [1,2,3]
|
|
264
|
+
data_ref = deep_ref(data)
|
|
265
|
+
|
|
266
|
+
assert data_ref.value is not data
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
通过 `to_raw` 可以获取原始对象
|
|
270
|
+
```python
|
|
271
|
+
from ex4nicegui import to_raw, deep_ref
|
|
272
|
+
|
|
273
|
+
data = [1, 2, 3]
|
|
274
|
+
data_ref = deep_ref(data)
|
|
275
|
+
|
|
276
|
+
assert data_ref.value is not data
|
|
277
|
+
assert to_raw(data_ref.value) is data
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
### `effect`
|
|
284
|
+
接受一个函数,自动监控函数中使用到的响应式对象变化,从而自动执行函数
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
a = to_ref(1)
|
|
288
|
+
b = to_ref("text")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@effect
|
|
292
|
+
def auto_run_when_ref_value():
|
|
293
|
+
print(f"a:{a.value}")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def change_value():
|
|
297
|
+
a.value = 2
|
|
298
|
+
b.value = "new text"
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
ui.button("change", on_click=change_value)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
首次执行 effect ,函数`auto_run_when_ref_value`将被执行一次.之后点击按钮,改变 `a` 的值(通过 `a.value`),函数`auto_run_when_ref_value`再次执行
|
|
305
|
+
|
|
306
|
+
> 切忌把大量数据处理逻辑分散在多个 `on` 或 `effect` 中,`on` 或 `effect` 中应该大部分为界面操作逻辑,而非响应式数据处理逻辑
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### `ref_computed`
|
|
311
|
+
与 `effect` 具备一样的功能,`ref_computed` 还能从函数中返回结果。一般用于从 `to_ref` 中进行二次计算
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
a = to_ref(1)
|
|
315
|
+
a_square = ref_computed(lambda: a.value * 2)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@effect
|
|
319
|
+
def effect1():
|
|
320
|
+
print(f"a_square:{a_square.value}")
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def change_value():
|
|
324
|
+
a.value = 2
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
ui.button("change", on_click=change_value)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
点击按钮后,`a.value` 值被修改,从而触发 `a_square` 重新计算.由于 `effect1` 中读取了 `a_square` 的值,从而触发 `effect1` 执行
|
|
331
|
+
|
|
332
|
+
> `ref_computed` 是只读的 `to_ref`
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
如果你更喜欢通过类组织代码,`ref_computed` 同样支持作用到实例方法上
|
|
336
|
+
|
|
337
|
+
```python
|
|
338
|
+
class MyState:
|
|
339
|
+
def __init__(self) -> None:
|
|
340
|
+
self.r_text = to_ref("")
|
|
341
|
+
|
|
342
|
+
@ref_computed
|
|
343
|
+
def post_text(self):
|
|
344
|
+
return self.r_text.value + "post"
|
|
345
|
+
|
|
346
|
+
state = MyState()
|
|
347
|
+
|
|
348
|
+
rxui.input(value=state.r_text)
|
|
349
|
+
rxui.label(state.post_text)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
### `async_computed`
|
|
355
|
+
二次计算中需要使用异步函数时,使用 `async_computed`
|
|
356
|
+
```python
|
|
357
|
+
|
|
358
|
+
# 模拟长时间执行的异步函数
|
|
359
|
+
async def long_time_query(input: str):
|
|
360
|
+
await asyncio.sleep(2)
|
|
361
|
+
num = random.randint(20, 100)
|
|
362
|
+
return f"query result[{input=}]:{num=}"
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
search = to_ref("")
|
|
366
|
+
evaluating = to_ref(False)
|
|
367
|
+
|
|
368
|
+
@async_computed(search, evaluating=evaluating, init="")
|
|
369
|
+
async def search_result():
|
|
370
|
+
return await long_time_query(search.value)
|
|
371
|
+
|
|
372
|
+
rxui.lazy_input(value=search)
|
|
373
|
+
|
|
374
|
+
rxui.label(
|
|
375
|
+
lambda: "查询中" if evaluating.value else "上方输入框输入内容并回车搜索"
|
|
376
|
+
)
|
|
377
|
+
rxui.label(search_result)
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
- `async_computed` 第一个参数必须明确指定需要监控的响应式数据. 使用列表可以同时指定多个响应式数据
|
|
382
|
+
- 参数 `evaluating` 为 bool 类型的响应式数据,当异步函数执行中,此变量值为 `True`,计算结束后为 `False`
|
|
383
|
+
- 参数 `init` 指定初始结果
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
### `on`
|
|
389
|
+
类似 `effect` 的功能,但是 `on` 需要明确指定监控的响应式对象
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
|
|
393
|
+
a1 = to_ref(1)
|
|
394
|
+
a2 = to_ref(10)
|
|
395
|
+
b = to_ref("text")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
@on(a1)
|
|
399
|
+
def watch_a1_only():
|
|
400
|
+
print(f"watch_a1_only ... a1:{a1.value},a2:{a2.value}")
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
@on([a1, b], onchanges=True)
|
|
404
|
+
def watch_a1_and_b():
|
|
405
|
+
print(f"watch_a1_and_b ... a1:{a1.value},a2:{a2.value},b:{b.value}")
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def change_a1():
|
|
409
|
+
a1.value += 1
|
|
410
|
+
ui.notify("change_a1")
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
ui.button("change a1", on_click=change_a1)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def change_a2():
|
|
417
|
+
a2.value += 1
|
|
418
|
+
ui.notify("change_a2")
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
ui.button("change a2", on_click=change_a2)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def change_b():
|
|
425
|
+
b.value += "x"
|
|
426
|
+
ui.notify("change_b")
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
ui.button("change b", on_click=change_b)
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
- 参数 `onchanges` 为 True 时(默认值为 False),指定的函数不会在绑定时执行
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
> 切忌把大量数据处理逻辑分散在多个 `on` 或 `effect` 中,`on` 或 `effect` 中应该大部分为界面操作逻辑,而非响应式数据处理逻辑
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
### `new_scope`
|
|
441
|
+
|
|
442
|
+
默认情况下,所有检测函数在客户端连接断开时自动销毁。如果需要更细粒度的控制,可以使用 `new_scope`
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
from nicegui import ui
|
|
446
|
+
from ex4nicegui import rxui, to_ref, effect, new_scope
|
|
447
|
+
|
|
448
|
+
a = to_ref(0.0)
|
|
449
|
+
|
|
450
|
+
scope1 = new_scope()
|
|
451
|
+
|
|
452
|
+
@scope1.run
|
|
453
|
+
def _():
|
|
454
|
+
@effect
|
|
455
|
+
def _():
|
|
456
|
+
print(f"scope 1:{a.value}")
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
rxui.number(value=a)
|
|
460
|
+
rxui.button("dispose scope 1", on_click=scope1.dispose)
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
## 组件功能
|
|
467
|
+
|
|
468
|
+
### vmodel
|
|
469
|
+
在表单输入元素或组件上创建双向绑定。
|
|
470
|
+
|
|
471
|
+
简单值类型的 `ref` 默认支持双向绑定
|
|
472
|
+
```python
|
|
473
|
+
from ex4nicegui import rxui, to_ref, deep_ref
|
|
474
|
+
|
|
475
|
+
data = to_ref("init")
|
|
476
|
+
|
|
477
|
+
rxui.label(lambda: f"{data.value=}")
|
|
478
|
+
# 默认就是双向绑定
|
|
479
|
+
rxui.input(value=data)
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
- 简单值类型一般是 `str`,`int` 等不可变值类型
|
|
483
|
+
|
|
484
|
+
当使用复杂数据结构时,会使用 `deep_ref` 保持嵌套值的响应性
|
|
485
|
+
```python
|
|
486
|
+
data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
|
|
487
|
+
|
|
488
|
+
rxui.label(lambda: f"{data.value=!s}")
|
|
489
|
+
|
|
490
|
+
# 当前版本没有任何绑定效果.或许未来的版本可以解决
|
|
491
|
+
rxui.input(value=data.value["a"])
|
|
492
|
+
|
|
493
|
+
# 只读绑定.其他途径修改了 `data.value["a"]` ,此输入框会同步,但反过来不行
|
|
494
|
+
rxui.input(value=lambda: data.value["a"])
|
|
495
|
+
|
|
496
|
+
# 要使用 vmodel 才能双向绑定
|
|
497
|
+
rxui.input(value=rxui.vmodel(data.value["a"]))
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
- 第一个输入框将完全失去响应性,因为代码等价于直接传入一个数值`1`
|
|
501
|
+
- 第二个输入框由于使用函数,将得到读取响应性(第三个输入框输入值,将得到同步)
|
|
502
|
+
- 第三个输入框,使用 `rxui.vmodel` 包裹,即可实现双向绑定
|
|
503
|
+
|
|
504
|
+
多数在配合 `vfor` 时使用 `vmodel`,可参考 [todo list 案例](./examples/todomvc/)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
### vfor
|
|
508
|
+
基于列表响应式数据,渲染列表组件。每项组件按需更新。数据项支持字典或任意类型对象
|
|
509
|
+
|
|
510
|
+
```python
|
|
511
|
+
from nicegui import ui
|
|
512
|
+
from ex4nicegui.reactive import rxui
|
|
513
|
+
from ex4nicegui import deep_ref, ref_computed
|
|
514
|
+
from typing import Dict
|
|
515
|
+
|
|
516
|
+
# refs
|
|
517
|
+
items = deep_ref(
|
|
518
|
+
[
|
|
519
|
+
{"id": 1, "message": "foo", "done": False},
|
|
520
|
+
{"id": 2, "message": "bar", "done": True},
|
|
521
|
+
]
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
# ref_computeds
|
|
525
|
+
@ref_computed
|
|
526
|
+
def done_count_info():
|
|
527
|
+
return f"done count:{sum(item['done'] for item in items.value)}"
|
|
528
|
+
|
|
529
|
+
# method
|
|
530
|
+
def check():
|
|
531
|
+
for item in items.value:
|
|
532
|
+
item["done"] = not item["done"]
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
# ui
|
|
536
|
+
rxui.label(done_count_info)
|
|
537
|
+
ui.button("check", on_click=check)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
@rxui.vfor(items,key='id')
|
|
541
|
+
def _(store: rxui.VforStore[Dict]):
|
|
542
|
+
# 函数中构建每一行数据的界面
|
|
543
|
+
item = store.get() # 通过 store.get 获取对应行的响应式对象(相当于每行的数据 to_ref(...))
|
|
544
|
+
mes = rxui.vmodel(item.value['message']) # 复杂结构默认没有双向绑定,需要使用 `vmodel`
|
|
545
|
+
|
|
546
|
+
# 输入框输入内容,可以看到单选框的标题同步变化
|
|
547
|
+
with ui.card():
|
|
548
|
+
with ui.row():
|
|
549
|
+
rxui.input(value=mes)
|
|
550
|
+
rxui.label(lambda: f"{mes.value=!s}")
|
|
551
|
+
rxui.checkbox(text=mes, value=rxui.vmodel(item.value['done']))
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
- `rxui.vfor` 装饰器到自定义函数
|
|
556
|
+
- 第一个参数传入响应式列表。列表中每一项可以是字典或其他对象(`dataclasses` 等等)
|
|
557
|
+
- 第二个参数 `key`: 为了可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你可以为每个元素对应的块提供一个唯一的 key 。默认情况使用列表元素索引。
|
|
558
|
+
- 自定义函数带有一个参数。通过 `store.get` 可以获取当前行的响应式对象
|
|
559
|
+
|
|
560
|
+
> vfor 渲染的项目,只有在新增数据时,才会创建
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
### 绑定类名
|
|
566
|
+
|
|
567
|
+
所有的组件类提供 `bind_classes` 用于绑定 `class`,支持三种不同的数据结构。
|
|
568
|
+
|
|
569
|
+
绑定字典
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
bg_color = to_ref(False)
|
|
573
|
+
has_error = to_ref(False)
|
|
574
|
+
|
|
575
|
+
rxui.label("test").bind_classes({"bg-blue": bg_color, "text-red": has_error})
|
|
576
|
+
|
|
577
|
+
rxui.switch("bg_color", value=bg_color)
|
|
578
|
+
rxui.switch("has_error", value=has_error)
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
字典键值为类名,对应值为 bool 的响应式变量。当响应式值为 `True`,类名应用到组件 class
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
绑定返回值为字典的响应式变量
|
|
587
|
+
|
|
588
|
+
```python
|
|
589
|
+
bg_color = to_ref(False)
|
|
590
|
+
has_error = to_ref(False)
|
|
591
|
+
|
|
592
|
+
class_obj = ref_computed(
|
|
593
|
+
lambda: {"bg-blue": bg_color.value, "text-red": has_error.value}
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
rxui.switch("bg_color", value=bg_color)
|
|
597
|
+
rxui.switch("has_error", value=has_error)
|
|
598
|
+
rxui.label("bind to ref_computed").bind_classes(class_obj)
|
|
599
|
+
# or direct function passing
|
|
600
|
+
rxui.label("bind to ref_computed").bind_classes(
|
|
601
|
+
lambda: {"bg-blue": bg_color.value, "text-red": has_error.value}
|
|
602
|
+
)
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
绑定为列表或单个字符串的响应式变量
|
|
608
|
+
|
|
609
|
+
```python
|
|
610
|
+
bg_color = to_ref("red")
|
|
611
|
+
bg_color_class = ref_computed(lambda: f"bg-{bg_color.value}")
|
|
612
|
+
|
|
613
|
+
text_color = to_ref("green")
|
|
614
|
+
text_color_class = ref_computed(lambda: f"text-{text_color.value}")
|
|
615
|
+
|
|
616
|
+
rxui.select(["red", "green", "yellow"], label="bg color", value=bg_color)
|
|
617
|
+
rxui.select(["red", "green", "yellow"], label="text color", value=text_color)
|
|
618
|
+
|
|
619
|
+
rxui.label("binding to arrays").bind_classes([bg_color_class, text_color_class])
|
|
620
|
+
rxui.label("binding to single string").bind_classes(bg_color_class)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
- 列表中每个元素为返回类名的响应式变量
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
### bind-style
|
|
629
|
+
|
|
630
|
+
```python
|
|
631
|
+
from nicegui import ui
|
|
632
|
+
from ex4nicegui.reactive import rxui
|
|
633
|
+
from ex4nicegui.utils.signals import to_ref
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
bg_color = to_ref("blue")
|
|
637
|
+
text_color = to_ref("red")
|
|
638
|
+
|
|
639
|
+
rxui.label("test").bind_style(
|
|
640
|
+
{
|
|
641
|
+
"background-color": bg_color,
|
|
642
|
+
"color": text_color,
|
|
643
|
+
}
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
rxui.select(["blue", "green", "yellow"], label="bg color", value=bg_color)
|
|
647
|
+
rxui.select(["red", "green", "yellow"], label="text color", value=text_color)
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
`bind_style` 传入字典,`key` 为样式名字,`value` 为样式值,响应式字符串
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
### bind_prop
|
|
655
|
+
|
|
656
|
+
绑定单个属性
|
|
657
|
+
|
|
658
|
+
```python
|
|
659
|
+
|
|
660
|
+
label = to_ref("hello")
|
|
661
|
+
|
|
662
|
+
rxui.button("").bind_prop("label", label)
|
|
663
|
+
# 允许使用函数
|
|
664
|
+
rxui.button("").bind_prop(
|
|
665
|
+
"label", lambda: f"{label.value} world"
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
rxui.input(value=label)
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
---
|
|
673
|
+
|
|
674
|
+
### rxui.echarts
|
|
675
|
+
使用 echarts 制作图表
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
679
|
+
#### rxui.echarts.from_javascript
|
|
680
|
+
从 javascript 代码创建 echart
|
|
681
|
+
|
|
682
|
+
```python
|
|
683
|
+
from pathlib import Path
|
|
684
|
+
|
|
685
|
+
rxui.echarts.from_javascript(Path("code.js"))
|
|
686
|
+
# or
|
|
687
|
+
rxui.echarts.from_javascript(
|
|
688
|
+
"""
|
|
689
|
+
(myChart) => {
|
|
690
|
+
|
|
691
|
+
option = {
|
|
692
|
+
xAxis: {
|
|
693
|
+
type: 'category',
|
|
694
|
+
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
695
|
+
},
|
|
696
|
+
yAxis: {
|
|
697
|
+
type: 'value'
|
|
698
|
+
},
|
|
699
|
+
series: [
|
|
700
|
+
{
|
|
701
|
+
data: [120, 200, 150, 80, 70, 110, 130],
|
|
702
|
+
type: 'bar'
|
|
703
|
+
}
|
|
704
|
+
]
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
myChart.setOption(option);
|
|
708
|
+
}
|
|
709
|
+
"""
|
|
710
|
+
)
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
- 函数第一个参数为 echart 实例对象.你需要在函数中通过 `setOption` 完成图表配置
|
|
714
|
+
|
|
715
|
+
函数也有第二个参数,为 `echarts` 全局对象,你可以通过 `echarts.registerMap` 注册地图。
|
|
716
|
+
|
|
717
|
+
```python
|
|
718
|
+
rxui.echarts.from_javascript(
|
|
719
|
+
"""
|
|
720
|
+
(chart,echarts) =>{
|
|
721
|
+
|
|
722
|
+
fetch('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json')
|
|
723
|
+
.then(response => response.json())
|
|
724
|
+
.then(data => {
|
|
725
|
+
echarts.registerMap('test_map', data);
|
|
726
|
+
|
|
727
|
+
chart.setOption({
|
|
728
|
+
geo: {
|
|
729
|
+
map: 'test_map',
|
|
730
|
+
roam: true,
|
|
731
|
+
},
|
|
732
|
+
tooltip: {},
|
|
733
|
+
legend: {},
|
|
734
|
+
series: [],
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
"""
|
|
739
|
+
)
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
#### rxui.echarts.register_map
|
|
745
|
+
注册地图.
|
|
746
|
+
|
|
747
|
+
```python
|
|
748
|
+
rxui.echarts.register_map(
|
|
749
|
+
"china", "https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json"
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
rxui.echarts(
|
|
753
|
+
{
|
|
754
|
+
"geo": {
|
|
755
|
+
"map": "china",
|
|
756
|
+
"roam": True,
|
|
757
|
+
},
|
|
758
|
+
"tooltip": {},
|
|
759
|
+
"legend": {},
|
|
760
|
+
"series": [],
|
|
761
|
+
}
|
|
762
|
+
)
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
- 参数 `map_name` 为自定义的地图名字。注意在图表配置中 `map` 必需对应注册的名字
|
|
766
|
+
- 参数 `src` 为有效的地图数据网络链接。
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
如果是 svg 数据,需要设置参数 `type="svg"`
|
|
770
|
+
```python
|
|
771
|
+
rxui.echarts.register_map("svg-rect", "/test/svg", type="svg")
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
你也可以直接提供本地地图数据的json文件路径对象(Path)
|
|
777
|
+
```python
|
|
778
|
+
from pathlib import Path
|
|
779
|
+
|
|
780
|
+
rxui.echarts.register_map(
|
|
781
|
+
"china", Path("map-data.json")
|
|
782
|
+
)
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
---
|
|
786
|
+
|
|
787
|
+
### gsap
|
|
788
|
+
js 动画库. [gsap文档](https://gsap.com/docs/v3/)
|
|
789
|
+
|
|
790
|
+
```python
|
|
791
|
+
from nicegui import ui
|
|
792
|
+
from ex4nicegui import gsap
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
#### gsap.from_
|
|
796
|
+
|
|
797
|
+
设置起始属性,动画将从设置的属性过渡到原始位置
|
|
798
|
+
|
|
799
|
+
```python
|
|
800
|
+
|
|
801
|
+
ui.label("test from").classes("target")
|
|
802
|
+
gsap.from_(".target", {"x": 50,'duration':1})
|
|
803
|
+
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
画面加载后,文本起始位置在往右偏移 50px 处,在 1秒 内移动到原始位置上
|
|
807
|
+
|
|
808
|
+
- 参数 `targets` 为 css 选择器
|
|
809
|
+
- 参数 `vars` 为属性值,具体参考 gsap 文档
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
#### gsap.to
|
|
814
|
+
|
|
815
|
+
设置结束属性,动画将从原始属性过渡到设置的属性
|
|
816
|
+
|
|
817
|
+
```python
|
|
818
|
+
|
|
819
|
+
ui.label("test to").classes("target")
|
|
820
|
+
gsap.to(".target", {"x": 50,'duration':1})
|
|
821
|
+
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
画面加载后,文本在 1秒 内,从原始位置往后移动 50px
|
|
825
|
+
|
|
826
|
+
- 参数 `targets` 为 css 选择器
|
|
827
|
+
- 参数 `vars` 为属性值,具体参考 gsap 文档
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
#### gsap.run_script
|
|
831
|
+
|
|
832
|
+
通过编写 js 设置动画
|
|
833
|
+
|
|
834
|
+
```python
|
|
835
|
+
|
|
836
|
+
gsap.run_script(
|
|
837
|
+
r"""function setGsap(gsap) {
|
|
838
|
+
gsap.to('.target',{"duration": 0.3,y:60})
|
|
839
|
+
}
|
|
840
|
+
""")
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
- 参数 `script` 可以为文本或 js 后缀的文件 `Path`
|
|
844
|
+
- 定义的 js 函数名字并不影响运行,第一个参数为 gsap 对象
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
### tab_panels
|
|
849
|
+
|
|
850
|
+
相比较于 `nicegui.ui.tab_panels` , `rxui.tab_panels` 没有参数 `tabs`。在数据响应式机制下,`tabs` 与 `tab_panels` 联动只需要通过参数 `value` 即可。
|
|
851
|
+
|
|
852
|
+
```python
|
|
853
|
+
from nicegui import ui
|
|
854
|
+
from ex4nicegui import rxui, to_ref
|
|
855
|
+
|
|
856
|
+
names = ["Tab 1", "Tab 2", "Tab 3"]
|
|
857
|
+
current_tab = to_ref(names[0])
|
|
858
|
+
|
|
859
|
+
with rxui.tabs(current_tab):
|
|
860
|
+
for name in names:
|
|
861
|
+
rxui.tab(name)
|
|
862
|
+
|
|
863
|
+
with rxui.tab_panels(current_tab):
|
|
864
|
+
for name in names:
|
|
865
|
+
with rxui.tab_panel(name):
|
|
866
|
+
ui.label(f"Content of {name}")
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
这是因为,数据响应机制下,组件联动是通过中间数据层(`to_ref`)实现的。因此,`tab_panels` 可以与其他组件联动(只需要保证使用同样的 `ref` 对象即可)
|
|
870
|
+
|
|
871
|
+
```python
|
|
872
|
+
names = ["Tab 1", "Tab 2", "Tab 3"]
|
|
873
|
+
current_tab = to_ref(names[0])
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
with rxui.tab_panels(current_tab):
|
|
877
|
+
for name in names:
|
|
878
|
+
with rxui.tab_panel(name):
|
|
879
|
+
ui.label(f"Content of {name}")
|
|
880
|
+
|
|
881
|
+
# tabs 不必在 panels 前面
|
|
882
|
+
with rxui.tabs(current_tab):
|
|
883
|
+
for name in names:
|
|
884
|
+
rxui.tab(name)
|
|
885
|
+
|
|
886
|
+
rxui.select(names, value=current_tab)
|
|
887
|
+
rxui.radio(names, value=current_tab).props("inline")
|
|
888
|
+
|
|
889
|
+
rxui.label(lambda: f"当前 tab 为:{current_tab.value}")
|
|
890
|
+
```
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
### scoped_style
|
|
894
|
+
|
|
895
|
+
`scoped_style` 方法允许你创建限定在组件内部的样式。
|
|
896
|
+
|
|
897
|
+
```python
|
|
898
|
+
# 所有子元素都会有红色轮廓,但排除自身
|
|
899
|
+
with rxui.row().scoped_style("*", "outline: 1px solid red;") as row:
|
|
900
|
+
ui.label("Hello")
|
|
901
|
+
ui.label("World")
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
# 所有子元素都会有红色轮廓,包括自身
|
|
905
|
+
with rxui.row().scoped_style(":self *", "outline: 1px solid red;") as row:
|
|
906
|
+
ui.label("Hello")
|
|
907
|
+
ui.label("World")
|
|
908
|
+
|
|
909
|
+
# 当鼠标悬停在 row 组件时,所有子元素都会有红色轮廓,但排除自身
|
|
910
|
+
with rxui.row().scoped_style(":hover *", "outline: 1px solid red;") as row:
|
|
911
|
+
ui.label("Hello")
|
|
912
|
+
ui.label("World")
|
|
913
|
+
|
|
914
|
+
# 当鼠标悬停在 row 组件时,所有子元素都会有红色轮廓,包括自身
|
|
915
|
+
with rxui.row().scoped_style(":self:hover *", "outline: 1px solid red;") as row:
|
|
916
|
+
ui.label("Hello")
|
|
917
|
+
ui.label("World")
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
---
|
|
922
|
+
|
|
923
|
+
## BI 模块
|
|
924
|
+
|
|
925
|
+
以最精简的 apis 创建可交互的数据可视化报表
|
|
926
|
+
|
|
927
|
+

|
|
928
|
+
|
|
929
|
+
```python
|
|
930
|
+
from nicegui import ui
|
|
931
|
+
import pandas as pd
|
|
932
|
+
import numpy as np
|
|
933
|
+
from ex4nicegui import bi
|
|
934
|
+
from ex4nicegui.reactive import rxui
|
|
935
|
+
from ex4nicegui import effect, effect_refreshable
|
|
936
|
+
from pyecharts.charts import Bar
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
# data ready
|
|
940
|
+
def gen_data():
|
|
941
|
+
np.random.seed(265)
|
|
942
|
+
field1 = ["a1", "a2", "a3", "a4"]
|
|
943
|
+
field2 = [f"name{i}" for i in range(1, 11)]
|
|
944
|
+
df = (
|
|
945
|
+
pd.MultiIndex.from_product([field1, field2], names=["cat", "name"])
|
|
946
|
+
.to_frame()
|
|
947
|
+
.reset_index(drop=True)
|
|
948
|
+
)
|
|
949
|
+
df[["idc1", "idc2"]] = np.random.randint(50, 1000, size=(len(df), 2))
|
|
950
|
+
return df
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
df = gen_data()
|
|
954
|
+
|
|
955
|
+
# 创建数据源
|
|
956
|
+
ds = bi.data_source(df)
|
|
957
|
+
|
|
958
|
+
# ui
|
|
959
|
+
ui.query(".nicegui-content").classes("items-stretch no-wrap")
|
|
960
|
+
|
|
961
|
+
with ui.row().classes("justify-evenly"):
|
|
962
|
+
# 基于数据源 `ds` 创建界面组件
|
|
963
|
+
ds.ui_select("cat").classes("min-w-[10rem]")
|
|
964
|
+
ds.ui_select("name").classes("min-w-[10rem]")
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
with ui.grid(columns=2):
|
|
968
|
+
# 使用字典配置图表
|
|
969
|
+
@ds.ui_echarts
|
|
970
|
+
def bar1(data: pd.DataFrame):
|
|
971
|
+
data = data.groupby("name").agg({"idc1": "sum", "idc2": "sum"}).reset_index()
|
|
972
|
+
|
|
973
|
+
return {
|
|
974
|
+
"xAxis": {"type": "value"},
|
|
975
|
+
"yAxis": {
|
|
976
|
+
"type": "category",
|
|
977
|
+
"data": data["name"].tolist(),
|
|
978
|
+
"inverse": True,
|
|
979
|
+
},
|
|
980
|
+
"legend": {"textStyle": {"color": "gray"}},
|
|
981
|
+
"series": [
|
|
982
|
+
{"type": "bar", "name": "idc1", "data": data["idc1"].tolist()},
|
|
983
|
+
{"type": "bar", "name": "idc2", "data": data["idc2"].tolist()},
|
|
984
|
+
],
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
bar1.classes("h-[20rem]")
|
|
988
|
+
|
|
989
|
+
# 使用pyecharts配置图表
|
|
990
|
+
@ds.ui_echarts
|
|
991
|
+
def bar2(data: pd.DataFrame):
|
|
992
|
+
data = data.groupby("name").agg({"idc1": "sum", "idc2": "sum"}).reset_index()
|
|
993
|
+
|
|
994
|
+
return (
|
|
995
|
+
Bar()
|
|
996
|
+
.add_xaxis(data["name"].tolist())
|
|
997
|
+
.add_yaxis("idc1", data["idc1"].tolist())
|
|
998
|
+
.add_yaxis("idc2", data["idc2"].tolist())
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
bar2.classes("h-[20rem]")
|
|
1002
|
+
|
|
1003
|
+
# 绑定点击事件,即可实现跳转
|
|
1004
|
+
@bar2.on_chart_click
|
|
1005
|
+
def _(e: rxui.echarts.EChartsMouseEventArguments):
|
|
1006
|
+
ui.open(f"/details/{e.name}", new_tab=True)
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
# 利用响应式机制,你可以随意组合原生 nicegui 组件
|
|
1010
|
+
label_a1_total = ui.label("")
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
# 当 ds 有变化,都会触发此函数
|
|
1014
|
+
@effect
|
|
1015
|
+
def _():
|
|
1016
|
+
# filtered_data 为过滤后的 DataFrame
|
|
1017
|
+
df = ds.filtered_data
|
|
1018
|
+
total = df[df["cat"] == "a1"]["idc1"].sum()
|
|
1019
|
+
label_a1_total.text = f"idc1 total(cat==a1):{total}"
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
# 你也可以使用 `effect_refreshable`,但需要注意函数中的组件每次都被重建
|
|
1023
|
+
@effect_refreshable
|
|
1024
|
+
def _():
|
|
1025
|
+
df = ds.filtered_data
|
|
1026
|
+
total = df[df["cat"] == "a2"]["idc1"].sum()
|
|
1027
|
+
ui.label(f"idc1 total(cat==a2):{total}")
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
# 当点击图表系列时,跳转的页面
|
|
1031
|
+
@ui.page("/details/{name}")
|
|
1032
|
+
def details_page(name: str):
|
|
1033
|
+
ui.label("This table data will not change")
|
|
1034
|
+
ui.aggrid.from_pandas(ds.data.query(f'name=="{name}"'))
|
|
1035
|
+
|
|
1036
|
+
ui.label("This table will change when the homepage data changes. ")
|
|
1037
|
+
|
|
1038
|
+
@bi.data_source
|
|
1039
|
+
def new_ds():
|
|
1040
|
+
return ds.filtered_data[["name", "idc1", "idc2"]]
|
|
1041
|
+
|
|
1042
|
+
new_ds.ui_aggrid()
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
ui.run()
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
### 细节
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
#### `bi.data_source`
|
|
1056
|
+
数据源是 BI 模块的核心概念,所有数据的联动基于此展开。当前版本(0.4.3)中,有两种创建数据源的方式
|
|
1057
|
+
|
|
1058
|
+
接收 `pandas` 的 `DataFrame`:
|
|
1059
|
+
```python
|
|
1060
|
+
from nicegui import ui
|
|
1061
|
+
from ex4nicegui import bi
|
|
1062
|
+
import pandas as pd
|
|
1063
|
+
|
|
1064
|
+
df = pd.DataFrame(
|
|
1065
|
+
{
|
|
1066
|
+
"name": list("aabcdf"),
|
|
1067
|
+
"cls": ["c1", "c2", "c1", "c1", "c3", None],
|
|
1068
|
+
"value": range(6),
|
|
1069
|
+
}
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
ds = bi.data_source(df)
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
有时候,我们希望基于另一个数据源创建新的数据源,此时可以使用装饰器创建联动数据源:
|
|
1077
|
+
```python
|
|
1078
|
+
df = pd.DataFrame(
|
|
1079
|
+
{
|
|
1080
|
+
"name": list("aabcdf"),
|
|
1081
|
+
"cls": ["c1", "c2", "c1", "c1", "c3", None],
|
|
1082
|
+
"value": range(6),
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
|
|
1086
|
+
ds = bi.data_source(df)
|
|
1087
|
+
|
|
1088
|
+
@bi.data_source
|
|
1089
|
+
def new_ds():
|
|
1090
|
+
# df is pd.DataFrame
|
|
1091
|
+
df = ds.filtered_data
|
|
1092
|
+
df=df.copy()
|
|
1093
|
+
df['value'] = df['value'] * 100
|
|
1094
|
+
return df
|
|
1095
|
+
|
|
1096
|
+
ds.ui_select('name')
|
|
1097
|
+
new_ds.ui_aggrid()
|
|
1098
|
+
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
注意,由于 `new_ds` 中使用了 `ds.filtered_data` ,因此 `ds` 的变动会触发 `new_ds` 的联动变化,从而导致 `new_ds` 创建的表格组件产生变化
|
|
1102
|
+
|
|
1103
|
+
---
|
|
1104
|
+
通过 `ds.remove_filters` 方法,移除所有筛选状态:
|
|
1105
|
+
```python
|
|
1106
|
+
ds = bi.data_source(df)
|
|
1107
|
+
|
|
1108
|
+
def on_remove_filters():
|
|
1109
|
+
ds.remove_filters()
|
|
1110
|
+
|
|
1111
|
+
ui.button("remove all filters", on_click=on_remove_filters)
|
|
1112
|
+
|
|
1113
|
+
ds.ui_select("name")
|
|
1114
|
+
ds.ui_aggrid()
|
|
1115
|
+
```
|
|
1116
|
+
---
|
|
1117
|
+
|
|
1118
|
+
通过 `ds.reload` 方法,重设数据源:
|
|
1119
|
+
```python
|
|
1120
|
+
|
|
1121
|
+
df = pd.DataFrame(
|
|
1122
|
+
{
|
|
1123
|
+
"name": list("aabcdf"),
|
|
1124
|
+
"cls": ["c1", "c2", "c1", "c1", "c3", None],
|
|
1125
|
+
"value": range(6),
|
|
1126
|
+
}
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
new_df = pd.DataFrame(
|
|
1130
|
+
{
|
|
1131
|
+
"name": list("xxyyds"),
|
|
1132
|
+
"cls": ["cla1", "cla2", "cla3", "cla3", "cla3", None],
|
|
1133
|
+
"value": range(100, 106),
|
|
1134
|
+
}
|
|
1135
|
+
)
|
|
1136
|
+
|
|
1137
|
+
ds = bi.data_source(df)
|
|
1138
|
+
|
|
1139
|
+
def on_remove_filters():
|
|
1140
|
+
ds.reload(new_df)
|
|
1141
|
+
|
|
1142
|
+
ui.button("reload data", on_click=on_remove_filters)
|
|
1143
|
+
|
|
1144
|
+
ds.ui_select("name")
|
|
1145
|
+
ds.ui_aggrid()
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
---
|
|
1149
|
+
#### ui_select
|
|
1150
|
+
|
|
1151
|
+
```python
|
|
1152
|
+
from nicegui import ui
|
|
1153
|
+
from ex4nicegui import bi
|
|
1154
|
+
import pandas as pd
|
|
1155
|
+
|
|
1156
|
+
df = pd.DataFrame(
|
|
1157
|
+
{
|
|
1158
|
+
"name": list("aabcdf"),
|
|
1159
|
+
"cls": ["c1", "c2", "c1", "c1", "c3", None],
|
|
1160
|
+
"value": range(6),
|
|
1161
|
+
}
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
ds = bi.data_source(df)
|
|
1165
|
+
|
|
1166
|
+
ds.ui_select("name")
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
第一个参数 column 指定数据源的列名
|
|
1170
|
+
|
|
1171
|
+
---
|
|
1172
|
+
通过参数 `sort_options` 设置选项顺序:
|
|
1173
|
+
```python
|
|
1174
|
+
ds.ui_select("name", sort_options={"value": "desc", "name": "asc"})
|
|
1175
|
+
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
---
|
|
1179
|
+
参数 `exclude_null_value` 设置是否排除空值:
|
|
1180
|
+
```python
|
|
1181
|
+
df = pd.DataFrame(
|
|
1182
|
+
{
|
|
1183
|
+
"cls": ["c1", "c2", "c1", "c1", "c3", None],
|
|
1184
|
+
}
|
|
1185
|
+
)
|
|
1186
|
+
|
|
1187
|
+
ds = bi.data_source(df)
|
|
1188
|
+
ds.ui_select("cls", exclude_null_value=True)
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
---
|
|
1192
|
+
|
|
1193
|
+
你可以通过关键字参数,设置原生 nicegui select 组件的参数.
|
|
1194
|
+
|
|
1195
|
+
通过 value 属性,设置默认值:
|
|
1196
|
+
```python
|
|
1197
|
+
ds.ui_select("cls",value=['c1','c2'])
|
|
1198
|
+
ds.ui_select("cls",multiple=False,value='c1')
|
|
1199
|
+
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
多选时(参数 `multiple` 默认为 True),`value` 需要指定为 list
|
|
1203
|
+
|
|
1204
|
+
单选时,`value` 设置为非 list
|
|
1205
|
+
|
|
1206
|
+
---
|
|
1207
|
+
|
|
1208
|
+
#### ui_table
|
|
1209
|
+
|
|
1210
|
+
表格
|
|
1211
|
+
|
|
1212
|
+
```python
|
|
1213
|
+
from nicegui import ui
|
|
1214
|
+
from ex4nicegui import bi
|
|
1215
|
+
import pandas as pd
|
|
1216
|
+
|
|
1217
|
+
data = pd.DataFrame({"name": ["f", "a", "c", "b"], "age": [1, 2, 3, 1]})
|
|
1218
|
+
ds = bi.data_source(data)
|
|
1219
|
+
|
|
1220
|
+
ds.ui_table(
|
|
1221
|
+
columns=[
|
|
1222
|
+
{"label": "new colA", "field": "colA", "sortable": True},
|
|
1223
|
+
]
|
|
1224
|
+
)
|
|
1225
|
+
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
- columns 与 nicegui `ui.table` 一致。其中 键值 `field` 对应数据源的列名,如果不存在,则该配置不会生效
|
|
1229
|
+
- rows 参数不会生效。因为表格的数据源始终由 data source 控制
|
|
1230
|
+
|
|
1231
|
+
---
|
|
1232
|
+
|
|
1233
|
+
#### ui_aggrid
|
|
1234
|
+
|
|
1235
|
+
|
|
1236
|
+
```python
|
|
1237
|
+
from nicegui import ui
|
|
1238
|
+
from ex4nicegui import bi
|
|
1239
|
+
import pandas as pd
|
|
1240
|
+
|
|
1241
|
+
data = pd.DataFrame(
|
|
1242
|
+
{
|
|
1243
|
+
"colA": list("abcde"),
|
|
1244
|
+
"colB": [f"n{idx}" for idx in range(5)],
|
|
1245
|
+
"colC": list(range(5)),
|
|
1246
|
+
}
|
|
1247
|
+
)
|
|
1248
|
+
df = pd.DataFrame(data)
|
|
1249
|
+
|
|
1250
|
+
source = bi.data_source(df)
|
|
1251
|
+
|
|
1252
|
+
source.ui_aggrid(
|
|
1253
|
+
options={
|
|
1254
|
+
"columnDefs": [
|
|
1255
|
+
{"headerName": "xx", "field": "no exists"},
|
|
1256
|
+
{"headerName": "new colA", "field": "colA"},
|
|
1257
|
+
{
|
|
1258
|
+
"field": "colC",
|
|
1259
|
+
"cellClassRules": {
|
|
1260
|
+
"bg-red-300": "x < 3",
|
|
1261
|
+
"bg-green-300": "x >= 3",
|
|
1262
|
+
},
|
|
1263
|
+
},
|
|
1264
|
+
],
|
|
1265
|
+
"rowData": [{"colX": [1, 2, 3, 4, 5]}],
|
|
1266
|
+
}
|
|
1267
|
+
)
|
|
1268
|
+
```
|
|
1269
|
+
|
|
1270
|
+
- 参数 options 与 nicegui `ui.aggrid` 一致。其中 `columnDefs` 中的键值 `field` 对应数据源的列名,如果不存在,则该配置不会生效
|
|
1271
|
+
- `rowData` 键值不会生效。因为表格的数据源始终由 data source 控制
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
|