Johnnyyyyy56 commited on
Commit
9571689
·
verified ·
1 Parent(s): 235058f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +345 -261
app.py CHANGED
@@ -309,7 +309,7 @@ def delete_images_in_category(emotion, image_type, confirm=False):
309
  deleted_count += 1
310
  except Exception as e:
311
  print(f"Error deleting {file}: {e}")
312
- failed_deletions.append(str(file))
313
 
314
  if deleted_count > 0 and LOG_FILE.exists():
315
  try:
@@ -388,31 +388,40 @@ def clear_all_data():
388
  empty_df = pd.DataFrame(columns=["timestamp", "batch_no", "emotion", "confidence", "face_path", "annotated_path"])
389
  return f"Deleted {deleted_count} items. All data has been cleared.", empty_df, None
390
 
391
- # Custom CSS for better layout
392
- custom_css = """
393
  :root {
394
- --spacing: 0.5rem;
395
  --border-radius: 8px;
396
- --shadow: 0 2px 4px rgba(0,0,0,0.1);
397
  --primary-color: #4f46e5;
398
  --danger-color: #ef4444;
399
  --success-color: #10b981;
 
400
  }
401
 
402
  .gradio-container {
403
- max-width: 100% !important;
404
  margin: 0 auto;
405
- padding: 1rem;
 
 
 
 
 
406
  }
407
 
408
  .message {
409
  color: red;
410
  font-weight: bold;
411
- margin-bottom: var(--spacing);
 
 
 
412
  }
413
 
414
  .gallery {
415
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)) !important;
416
  gap: var(--spacing);
417
  }
418
 
@@ -431,23 +440,31 @@ custom_css = """
431
  }
432
 
433
  .tab-nav {
434
- margin-bottom: 1rem;
435
  }
436
 
437
  .tab-content {
438
- padding: 1rem;
439
  background: white;
440
  border-radius: var(--border-radius);
441
  box-shadow: var(--shadow);
 
442
  }
443
 
444
  .input-group, .output-group {
445
- margin-bottom: var(--spacing);
446
  }
447
 
448
  button {
449
  border-radius: var(--border-radius) !important;
450
- padding: 0.5rem 1rem !important;
 
 
 
 
 
 
 
451
  }
452
 
453
  button.primary {
@@ -462,89 +479,137 @@ button.danger {
462
 
463
  .webcam-container {
464
  width: 100%;
465
- max-width: 640px;
466
  margin: 0 auto;
467
  border-radius: var(--border-radius);
468
  overflow: hidden;
 
469
  }
470
 
471
  .result-container {
472
  width: 100%;
473
- max-width: 640px;
474
- margin: 0 auto;
 
 
475
  }
476
 
477
- @media (max-width: 768px) {
478
- .gallery {
479
- grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  }
481
 
482
  .gradio-container {
483
- padding: 0.5rem;
 
 
 
 
 
 
484
  }
485
  }
486
  """
487
 
488
- # Capture Interface
489
- with gr.Blocks(title="Emotion Capture", css=custom_css) as capture_interface:
490
-
491
- gr.Markdown("""
492
- # Emotion Capture Interface
493
- <div style="margin-bottom: 1rem;">
494
- 1. Enter/scan your batch number (numbers only)<br>
495
- 2. System will automatically proceed after 5 seconds of inactivity<br>
496
- 3. Webcam will activate for face capture<br>
497
- 4. View your emotion analysis results<br>
498
- 5. Click "Done" to reset the interface
499
- </div>
500
- """)
501
-
502
- with gr.Row():
503
- batch_no = gr.Textbox(
504
- label="Batch Number",
505
- placeholder="Enter or scan numbers only",
506
- interactive=True,
507
- scale=3
508
- )
509
-
510
- message = gr.Textbox(
511
- label="Status",
512
- interactive=False,
513
- elem_classes="message",
514
- visible=False
515
- )
516
-
517
- with gr.Row():
518
- webcam = gr.Image(
519
- sources=["webcam"],
520
- type="pil",
521
- label="Face Capture",
522
- interactive=True,
523
- mirror_webcam=True,
524
- visible=False,
525
- elem_classes="webcam-container"
526
- )
527
-
528
- with gr.Row():
529
- result_img = gr.Image(
530
- label="Analysis Result",
531
- interactive=False,
532
- visible=False,
533
- elem_classes="result-container"
534
- )
535
 
536
- with gr.Row():
537
- result_text = gr.Textbox(
538
- label="Emotion Result",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  interactive=False,
 
540
  visible=False
541
  )
542
-
543
- with gr.Row():
544
- done_btn = gr.Button(
545
- "Done",
546
- visible=False
547
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
  # Detect when user stops typing (with 5 second delay)
550
  batch_no.change(
@@ -577,201 +642,220 @@ with gr.Blocks(title="Emotion Capture", css=custom_css) as capture_interface:
577
  outputs=[batch_no, message, webcam, result_img, result_text, done_btn]
578
  )
579
 
580
- # Data Management Interface
581
  with gr.Blocks(title="Data Management") as data_interface:
582
 
583
- gr.Markdown("# Data Management Interface")
584
-
585
- with gr.Tab("Image Management"):
586
- with gr.Column():
587
- gr.Markdown("## Select and Manage Images")
588
- with gr.Row():
589
- emotion_selector = gr.Dropdown(
590
- choices=["All Emotions"] + list(EMOTION_MAP.keys()),
591
- label="Emotion Category",
592
- value="All Emotions",
593
- scale=2
594
- )
595
- image_type_selector = gr.Dropdown(
596
- choices=["faces", "annotated"],
597
- label="Image Type",
598
- value="faces",
599
- scale=1
600
- )
601
- refresh_btn = gr.Button("Refresh Gallery", scale=1)
602
-
603
- current_image_paths = gr.State([])
604
-
605
- gallery = gr.Gallery(
606
- label="Image Gallery",
607
- columns=4,
608
- height="auto"
609
- )
610
- selected_images = gr.CheckboxGroup(
611
- label="Selected Images",
612
- interactive=True,
613
- value=[]
614
- )
615
-
616
- with gr.Row():
617
- with gr.Column(scale=1):
618
- gr.Markdown("### Download Options")
619
- with gr.Row():
620
- download_btn = gr.Button("Download Selected", variant="primary")
621
- download_all_btn = gr.Button("Download All in Category")
622
- download_structured_btn = gr.Button("Download All (Structured)", variant="primary")
623
- download_output = gr.File(label="Download Result", visible=False)
624
-
625
- with gr.Column(scale=1):
626
- gr.Markdown("### Delete Options")
627
- delete_btn = gr.Button("Delete Selected", variant="stop")
628
  with gr.Row():
629
- delete_confirm = gr.Checkbox(label="I confirm I want to delete ALL images in this category", value=False, scale=3)
630
- delete_all_btn = gr.Button("Delete All in Category", variant="stop", interactive=False, scale=1)
631
- delete_output = gr.Textbox(label="Delete Status")
632
-
633
- def update_gallery_components(emotion, image_type):
634
- image_dict = get_image_gallery(emotion, image_type)
635
- gallery_items = []
636
- image_paths = []
637
- for emotion, images in image_dict.items():
638
- for img_path in images:
639
- gallery_items.append((img_path, f"{emotion}: {Path(img_path).name}"))
640
- image_paths.append(img_path)
641
- return gallery_items, image_paths
642
-
643
- initial_gallery, initial_paths = update_gallery_components("All Emotions", "faces")
644
- gallery.value = initial_gallery
645
- current_image_paths.value = initial_paths
646
- selected_images.choices = initial_paths
647
-
648
- def update_components(emotion, image_type):
649
- gallery_items, image_paths = update_gallery_components(emotion, image_type)
650
- return {
651
- gallery: gallery_items,
652
- current_image_paths: image_paths,
653
- selected_images: gr.CheckboxGroup(choices=image_paths, value=[])
654
- }
655
-
656
- emotion_selector.change(
657
- update_components,
658
- inputs=[emotion_selector, image_type_selector],
659
- outputs=[gallery, current_image_paths, selected_images]
660
- )
661
-
662
- image_type_selector.change(
663
- update_components,
664
- inputs=[emotion_selector, image_type_selector],
665
- outputs=[gallery, current_image_paths, selected_images]
666
- )
667
-
668
- refresh_btn.click(
669
- update_components,
670
- inputs=[emotion_selector, image_type_selector],
671
- outputs=[gallery, current_image_paths, selected_images]
672
- )
673
-
674
- download_btn.click(
675
- lambda selected: create_custom_zip(selected),
676
- inputs=selected_images,
677
- outputs=download_output,
678
- api_name="download_selected"
679
- ).then(
680
- lambda x: gr.File(visible=x is not None),
681
- inputs=download_output,
682
- outputs=download_output
683
- )
684
 
685
- download_all_btn.click(
686
- lambda emotion, img_type: create_custom_zip(
687
- [str(f) for f in (SAVE_DIR / img_type / (emotion if emotion != "All Emotions" else "*")).glob("*.jpg") if f.exists()]
688
- ),
689
- inputs=[emotion_selector, image_type_selector],
690
- outputs=download_output,
691
- api_name="download_all"
692
- ).then(
693
- lambda x: gr.File(visible=x is not None),
694
- inputs=download_output,
695
- outputs=download_output
696
- )
697
-
698
- download_structured_btn.click(
699
- download_all_emotions_structured,
700
- outputs=download_output,
701
- api_name="download_all_structured"
702
- ).then(
703
- lambda x: gr.File(visible=x is not None),
704
- inputs=download_output,
705
- outputs=download_output
706
- )
707
-
708
- delete_btn.click(
709
- lambda selected: {
710
- "delete_output": delete_selected_images(selected),
711
- **update_components(emotion_selector.value, image_type_selector.value)
712
- },
713
- inputs=selected_images,
714
- outputs=[delete_output, gallery, current_image_paths, selected_images]
715
- )
716
-
717
- delete_confirm.change(
718
- lambda x: gr.Button(interactive=x),
719
- inputs=delete_confirm,
720
- outputs=delete_all_btn
721
- )
722
-
723
- delete_all_btn.click(
724
- lambda emotion, img_type, confirm: {
725
- "delete_output": delete_images_in_category(emotion, img_type, confirm),
726
- **update_components(emotion, img_type)
727
- },
728
- inputs=[emotion_selector, image_type_selector, delete_confirm],
729
- outputs=[delete_output, gallery, current_image_paths, selected_images]
730
- )
731
 
732
- with gr.Tab("Emotion Logs"):
733
- with gr.Column():
734
- gr.Markdown("## Emotion Analysis Logs")
735
- with gr.Row():
736
- refresh_logs_btn = gr.Button("Refresh Logs")
737
- download_logs_btn = gr.Button("Download Logs as CSV")
738
- clear_all_btn = gr.Button("Clear All Data", variant="stop")
739
-
740
- logs_display = gr.Markdown()
741
- logs_csv = gr.File(label="Logs Download", visible=False)
742
- clear_message = gr.Textbox(label="Status", interactive=False)
743
-
744
- refresh_logs_btn.click(
745
- view_logs,
746
- outputs=logs_display
747
- )
748
-
749
- download_logs_btn.click(
750
- download_logs,
751
- outputs=logs_csv,
752
- api_name="download_logs"
753
- ).then(
754
- lambda x: gr.File(visible=x is not None),
755
- inputs=logs_csv,
756
- outputs=logs_csv
757
- )
758
-
759
- clear_all_btn.click(
760
- clear_all_data,
761
- outputs=[clear_message, logs_display, logs_csv]
762
- ).then(
763
- lambda: update_components("All Emotions", "faces"),
764
- outputs=[gallery, current_image_paths]
765
- ).then(
766
- lambda: gr.CheckboxGroup(choices=[], value=[]),
767
- outputs=selected_images
768
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
 
770
  # Combine interfaces
771
  demo = gr.TabbedInterface(
772
  [capture_interface, data_interface],
773
  ["Emotion Capture", "Data Management"],
774
- css=custom_css
775
  )
776
 
777
  if __name__ == "__main__":
 
309
  deleted_count += 1
310
  except Exception as e:
311
  print(f"Error deleting {file}: {e}")
312
+ failed_deletions.append(str(file))
313
 
314
  if deleted_count > 0 and LOG_FILE.exists():
315
  try:
 
388
  empty_df = pd.DataFrame(columns=["timestamp", "batch_no", "emotion", "confidence", "face_path", "annotated_path"])
389
  return f"Deleted {deleted_count} items. All data has been cleared.", empty_df, None
390
 
391
+ # Desktop-Optimized CSS
392
+ desktop_css = """
393
  :root {
394
+ --spacing: 0.75rem;
395
  --border-radius: 8px;
396
+ --shadow: 0 2px 6px rgba(0,0,0,0.1);
397
  --primary-color: #4f46e5;
398
  --danger-color: #ef4444;
399
  --success-color: #10b981;
400
+ --panel-bg: #f8f9fa;
401
  }
402
 
403
  .gradio-container {
404
+ max-width: 1200px !important;
405
  margin: 0 auto;
406
+ padding: 1.5rem;
407
+ }
408
+
409
+ h1 {
410
+ font-size: 1.8rem !important;
411
+ margin-bottom: 1.2rem !important;
412
  }
413
 
414
  .message {
415
  color: red;
416
  font-weight: bold;
417
+ margin: 0.5rem 0;
418
+ padding: 0.5rem;
419
+ background: #fff3f3;
420
+ border-radius: var(--border-radius);
421
  }
422
 
423
  .gallery {
424
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) !important;
425
  gap: var(--spacing);
426
  }
427
 
 
440
  }
441
 
442
  .tab-nav {
443
+ margin-bottom: 1.5rem;
444
  }
445
 
446
  .tab-content {
447
+ padding: 1.5rem;
448
  background: white;
449
  border-radius: var(--border-radius);
450
  box-shadow: var(--shadow);
451
+ margin-bottom: 1.5rem;
452
  }
453
 
454
  .input-group, .output-group {
455
+ margin-bottom: 1rem;
456
  }
457
 
458
  button {
459
  border-radius: var(--border-radius) !important;
460
+ padding: 0.6rem 1.2rem !important;
461
+ font-size: 0.95rem !important;
462
+ transition: all 0.2s ease !important;
463
+ }
464
+
465
+ button:hover {
466
+ transform: translateY(-1px);
467
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
468
  }
469
 
470
  button.primary {
 
479
 
480
  .webcam-container {
481
  width: 100%;
482
+ max-width: 800px;
483
  margin: 0 auto;
484
  border-radius: var(--border-radius);
485
  overflow: hidden;
486
+ box-shadow: var(--shadow);
487
  }
488
 
489
  .result-container {
490
  width: 100%;
491
+ max-width: 800px;
492
+ margin: 1rem auto;
493
+ border-radius: var(--border-radius);
494
+ overflow: hidden;
495
  }
496
 
497
+ .instruction-panel {
498
+ background: var(--panel-bg);
499
+ padding: 1.2rem;
500
+ border-radius: var(--border-radius);
501
+ margin-bottom: 1.5rem;
502
+ border-left: 4px solid var(--primary-color);
503
+ }
504
+
505
+ .control-row {
506
+ display: flex;
507
+ gap: 1rem;
508
+ align-items: center;
509
+ margin-bottom: 1rem;
510
+ }
511
+
512
+ .dashboard-panel {
513
+ background: white;
514
+ padding: 1.5rem;
515
+ border-radius: var(--border-radius);
516
+ box-shadow: var(--shadow);
517
+ margin-bottom: 1.5rem;
518
+ }
519
+
520
+ .management-section {
521
+ display: grid;
522
+ grid-template-columns: 1fr 1fr;
523
+ gap: 1.5rem;
524
+ margin-top: 1.5rem;
525
+ }
526
+
527
+ @media (max-width: 992px) {
528
+ .management-section {
529
+ grid-template-columns: 1fr;
530
  }
531
 
532
  .gradio-container {
533
+ padding: 1rem;
534
+ }
535
+ }
536
+
537
+ @media (max-width: 768px) {
538
+ .gallery {
539
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)) !important;
540
  }
541
  }
542
  """
543
 
544
+ # Capture Interface - Desktop Optimized
545
+ with gr.Blocks(title="Emotion Capture", css=desktop_css) as capture_interface:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
 
547
+ with gr.Column():
548
+ gr.Markdown("""
549
+ # Emotion Capture Interface
550
+ """)
551
+
552
+ with gr.Column(elem_classes="instruction-panel"):
553
+ gr.Markdown("""
554
+ **Instructions:**
555
+ 1. Enter/scan your batch number (numbers only)
556
+ 2. System will automatically proceed after 5 seconds of inactivity
557
+ 3. Webcam will activate for face capture
558
+ 4. View your emotion analysis results
559
+ 5. Click "Done" to reset the interface
560
+ """)
561
+
562
+ with gr.Row(elem_classes="control-row"):
563
+ batch_no = gr.Textbox(
564
+ label="Batch Number",
565
+ placeholder="Enter or scan numbers only",
566
+ interactive=True,
567
+ scale=4
568
+ )
569
+
570
+ message = gr.Textbox(
571
+ label="Status",
572
  interactive=False,
573
+ elem_classes="message",
574
  visible=False
575
  )
576
+
577
+ with gr.Row():
578
+ with gr.Column(scale=1):
579
+ webcam = gr.Image(
580
+ sources=["webcam"],
581
+ type="pil",
582
+ label="Live Camera Feed",
583
+ interactive=True,
584
+ mirror_webcam=True,
585
+ visible=False,
586
+ elem_classes="webcam-container",
587
+ height=400
588
+ )
589
+
590
+ with gr.Column(scale=1):
591
+ result_img = gr.Image(
592
+ label="Analysis Result",
593
+ interactive=False,
594
+ visible=False,
595
+ elem_classes="result-container",
596
+ height=400
597
+ )
598
+
599
+ with gr.Row():
600
+ result_text = gr.Textbox(
601
+ label="Emotion Result",
602
+ interactive=False,
603
+ visible=False,
604
+ container=False
605
+ )
606
+
607
+ with gr.Row():
608
+ done_btn = gr.Button(
609
+ "Done",
610
+ visible=False,
611
+ elem_classes="primary"
612
+ )
613
 
614
  # Detect when user stops typing (with 5 second delay)
615
  batch_no.change(
 
642
  outputs=[batch_no, message, webcam, result_img, result_text, done_btn]
643
  )
644
 
645
+ # Data Management Interface - Desktop Optimized
646
  with gr.Blocks(title="Data Management") as data_interface:
647
 
648
+ with gr.Column():
649
+ gr.Markdown("""
650
+ # Data Management Dashboard
651
+ """)
652
+
653
+ with gr.Tabs():
654
+ with gr.Tab("Image Management", elem_classes="dashboard-panel"):
655
+ with gr.Column():
656
+ gr.Markdown("### Image Gallery Management")
657
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  with gr.Row():
659
+ emotion_selector = gr.Dropdown(
660
+ choices=["All Emotions"] + list(EMOTION_MAP.keys()),
661
+ label="Emotion Category",
662
+ value="All Emotions",
663
+ scale=3
664
+ )
665
+ image_type_selector = gr.Dropdown(
666
+ choices=["faces", "annotated"],
667
+ label="Image Type",
668
+ value="faces",
669
+ scale=2
670
+ )
671
+ refresh_btn = gr.Button("Refresh", scale=1)
672
+
673
+ current_image_paths = gr.State([])
674
+
675
+ gallery = gr.Gallery(
676
+ label="Image Gallery",
677
+ columns=5,
678
+ height="auto",
679
+ preview=True
680
+ )
681
+
682
+ selected_images = gr.CheckboxGroup(
683
+ label="Selected Images",
684
+ interactive=True,
685
+ value=[],
686
+ visible=False
687
+ )
688
+
689
+ with gr.Row(elem_classes="management-section"):
690
+ with gr.Column():
691
+ gr.Markdown("#### Download Options")
692
+ with gr.Row():
693
+ download_btn = gr.Button("Download Selected", variant="primary")
694
+ download_all_btn = gr.Button("Download All in Category")
695
+ download_structured_btn = gr.Button("Download Full Archive", variant="primary")
696
+ download_output = gr.File(label="Download Result", visible=False)
697
+
698
+ with gr.Column():
699
+ gr.Markdown("#### Delete Options")
700
+ delete_btn = gr.Button("Delete Selected", variant="stop")
701
+ with gr.Row():
702
+ delete_confirm = gr.Checkbox(
703
+ label="Confirm deletion of ALL images in this category",
704
+ value=False,
705
+ scale=4
706
+ )
707
+ delete_all_btn = gr.Button(
708
+ "Delete All in Category",
709
+ variant="stop",
710
+ interactive=False,
711
+ scale=1
712
+ )
713
+ delete_output = gr.Textbox(label="Operation Status")
714
 
715
+ with gr.Tab("Emotion Logs", elem_classes="dashboard-panel"):
716
+ with gr.Column():
717
+ gr.Markdown("### Emotion Analysis Logs")
718
+
719
+ with gr.Row():
720
+ refresh_logs_btn = gr.Button("Refresh Logs")
721
+ download_logs_btn = gr.Button("Export Logs", variant="primary")
722
+ clear_all_btn = gr.Button("Clear All Data", variant="stop")
723
+
724
+ logs_display = gr.Markdown()
725
+ logs_csv = gr.File(label="Logs Download", visible=False)
726
+ clear_message = gr.Textbox(label="Operation Status")
727
+
728
+ # Event handlers for Data Management
729
+ def update_gallery_components(emotion, image_type):
730
+ image_dict = get_image_gallery(emotion, image_type)
731
+ gallery_items = []
732
+ image_paths = []
733
+ for emotion, images in image_dict.items():
734
+ for img_path in images:
735
+ gallery_items.append((img_path, f"{emotion}: {Path(img_path).name}"))
736
+ image_paths.append(img_path)
737
+ return gallery_items, image_paths
738
+
739
+ initial_gallery, initial_paths = update_gallery_components("All Emotions", "faces")
740
+ gallery.value = initial_gallery
741
+ current_image_paths.value = initial_paths
742
+ selected_images.choices = initial_paths
743
+
744
+ def update_components(emotion, image_type):
745
+ gallery_items, image_paths = update_gallery_components(emotion, image_type)
746
+ return {
747
+ gallery: gallery_items,
748
+ current_image_paths: image_paths,
749
+ selected_images: gr.CheckboxGroup(choices=image_paths, value=[])
750
+ }
751
+
752
+ emotion_selector.change(
753
+ update_components,
754
+ inputs=[emotion_selector, image_type_selector],
755
+ outputs=[gallery, current_image_paths, selected_images]
756
+ )
 
 
 
 
757
 
758
+ image_type_selector.change(
759
+ update_components,
760
+ inputs=[emotion_selector, image_type_selector],
761
+ outputs=[gallery, current_image_paths, selected_images]
762
+ )
763
+
764
+ refresh_btn.click(
765
+ update_components,
766
+ inputs=[emotion_selector, image_type_selector],
767
+ outputs=[gallery, current_image_paths, selected_images]
768
+ )
769
+
770
+ download_btn.click(
771
+ lambda selected: create_custom_zip(selected),
772
+ inputs=selected_images,
773
+ outputs=download_output,
774
+ api_name="download_selected"
775
+ ).then(
776
+ lambda x: gr.File(visible=x is not None),
777
+ inputs=download_output,
778
+ outputs=download_output
779
+ )
780
+
781
+ download_all_btn.click(
782
+ lambda emotion, img_type: create_custom_zip(
783
+ [str(f) for f in (SAVE_DIR / img_type / (emotion if emotion != "All Emotions" else "*")).glob("*.jpg") if f.exists()]
784
+ ),
785
+ inputs=[emotion_selector, image_type_selector],
786
+ outputs=download_output,
787
+ api_name="download_all"
788
+ ).then(
789
+ lambda x: gr.File(visible=x is not None),
790
+ inputs=download_output,
791
+ outputs=download_output
792
+ )
793
+
794
+ download_structured_btn.click(
795
+ download_all_emotions_structured,
796
+ outputs=download_output,
797
+ api_name="download_all_structured"
798
+ ).then(
799
+ lambda x: gr.File(visible=x is not None),
800
+ inputs=download_output,
801
+ outputs=download_output
802
+ )
803
+
804
+ delete_btn.click(
805
+ lambda selected: {
806
+ "delete_output": delete_selected_images(selected),
807
+ **update_components(emotion_selector.value, image_type_selector.value)
808
+ },
809
+ inputs=selected_images,
810
+ outputs=[delete_output, gallery, current_image_paths, selected_images]
811
+ )
812
+
813
+ delete_confirm.change(
814
+ lambda x: gr.Button(interactive=x),
815
+ inputs=delete_confirm,
816
+ outputs=delete_all_btn
817
+ )
818
+
819
+ delete_all_btn.click(
820
+ lambda emotion, img_type, confirm: {
821
+ "delete_output": delete_images_in_category(emotion, img_type, confirm),
822
+ **update_components(emotion, img_type)
823
+ },
824
+ inputs=[emotion_selector, image_type_selector, delete_confirm],
825
+ outputs=[delete_output, gallery, current_image_paths, selected_images]
826
+ )
827
+
828
+ refresh_logs_btn.click(
829
+ view_logs,
830
+ outputs=logs_display
831
+ )
832
+
833
+ download_logs_btn.click(
834
+ download_logs,
835
+ outputs=logs_csv,
836
+ api_name="download_logs"
837
+ ).then(
838
+ lambda x: gr.File(visible=x is not None),
839
+ inputs=logs_csv,
840
+ outputs=logs_csv
841
+ )
842
+
843
+ clear_all_btn.click(
844
+ clear_all_data,
845
+ outputs=[clear_message, logs_display, logs_csv]
846
+ ).then(
847
+ lambda: update_components("All Emotions", "faces"),
848
+ outputs=[gallery, current_image_paths]
849
+ ).then(
850
+ lambda: gr.CheckboxGroup(choices=[], value=[]),
851
+ outputs=selected_images
852
+ )
853
 
854
  # Combine interfaces
855
  demo = gr.TabbedInterface(
856
  [capture_interface, data_interface],
857
  ["Emotion Capture", "Data Management"],
858
+ css=desktop_css
859
  )
860
 
861
  if __name__ == "__main__":