Kimilhee commited on
Commit
19d1c4b
·
1 Parent(s): 123f654

승리 이미지 선택할 때, 애니메이션 추가.

Browse files
Files changed (3) hide show
  1. .cursorrules +129 -0
  2. app.py +201 -23
  3. static/beauty/out-5.webp +0 -0
.cursorrules ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are an expert in Python project development, specializing in building well-structured, maintainable Python applications.
2
+
3
+ Core Expertise:
4
+
5
+ - Python Development
6
+ - Project Architecture
7
+ - Testing Strategies
8
+ - Code Quality
9
+ - Package Management
10
+ - Gradio
11
+
12
+ Development Guidelines:
13
+
14
+ 1. Project Structure
15
+ ALWAYS:
16
+
17
+ - Use proper package layout
18
+ - Implement modular design
19
+ - Follow Python standards
20
+ - Use proper configuration
21
+ - Maintain documentation
22
+
23
+ NEVER:
24
+
25
+ - Mix package boundaries
26
+ - Skip project structure
27
+ - Ignore Python standards
28
+ - Use flat structure
29
+
30
+ 2. Code Organization
31
+ ALWAYS:
32
+
33
+ - Use proper imports
34
+ - Implement clean architecture
35
+ - Follow SOLID principles
36
+ - Use type hints
37
+ - Document code properly
38
+
39
+ NEVER:
40
+
41
+ - Use circular imports
42
+ - Mix responsibilities
43
+ - Skip type annotations
44
+ - Ignore documentation
45
+
46
+ 3. Dependency Management
47
+ ALWAYS:
48
+
49
+ - Use virtual environments
50
+ - Pin dependencies
51
+ - Use requirements files
52
+ - Handle dev dependencies
53
+ - Update regularly
54
+
55
+ NEVER:
56
+
57
+ - Mix environment dependencies
58
+ - Use global packages
59
+ - Skip version pinning
60
+ - Ignore security updates
61
+
62
+ 4. Testing Strategy
63
+ ALWAYS:
64
+
65
+ - Write unit tests
66
+ - Implement integration tests
67
+ - Use proper fixtures
68
+ - Test edge cases
69
+ - Measure coverage
70
+
71
+ NEVER:
72
+
73
+ - Skip test documentation
74
+ - Mix test types
75
+ - Ignore test isolation
76
+ - Skip error scenarios
77
+
78
+ Code Quality:
79
+
80
+ - Use proper linting
81
+ - Implement formatting
82
+ - Follow style guides
83
+ - Use static analysis
84
+ - Monitor complexity
85
+
86
+ Documentation:
87
+
88
+ - Write clear docstrings
89
+ - Maintain README
90
+ - Document APIs
91
+ - Include examples
92
+ - Keep docs updated
93
+
94
+ Development Tools:
95
+
96
+ - Use proper IDE
97
+ - Configure debugger
98
+ - Use version control
99
+ - Implement CI/CD
100
+ - Use code analysis
101
+
102
+ Best Practices:
103
+
104
+ - Follow PEP standards
105
+ - Keep code clean
106
+ - Handle errors properly
107
+ - Use proper logging
108
+ - Implement monitoring
109
+
110
+ Package Distribution:
111
+
112
+ - Use proper packaging
113
+ - Handle versioning
114
+ - Write setup files
115
+ - Include metadata
116
+ - Document installation
117
+
118
+ Remember:
119
+
120
+ - Focus on maintainability
121
+ - Keep code organized
122
+ - Handle errors properly
123
+ - Document thoroughly
124
+
125
+ Gradio Integration:
126
+
127
+ - Create interactive demos using Gradio for model inference and visualization.
128
+ - Design user-friendly interfaces that showcase model capabilities.
129
+ - Implement proper error handling and input validation in Gradio apps.
app.py CHANGED
@@ -4,6 +4,7 @@ from random import shuffle
4
  import os
5
  import gradio as gr
6
  import replicate
 
7
 
8
  # .env 파일 로드
9
  load_dotenv()
@@ -354,8 +355,111 @@ footer {visibility: hidden}
354
  .tiny-input {
355
  width: '20px',
356
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  """
358
 
 
 
359
  # gr.set_static_paths(paths=["static/image/", "static/examples/"])
360
 
361
  # theme = "NoCrypt/miku" # 하늘색 테마
@@ -491,6 +595,7 @@ def setup_demo():
491
  interactive=False,
492
  # height=768,
493
  )
 
494
 
495
  @entryListGallery.select(outputs=selectedImageIndex)
496
  def setSelectedImageIndex(selection: gr.SelectData):
@@ -501,7 +606,11 @@ def setup_demo():
501
  return gr.update(value=entryList), entryList
502
 
503
  del entryList[selectedIndex]
504
- return entryList, entryList
 
 
 
 
505
 
506
  # 왠지 모르게 마지막 요소를 지우면 preview=True 가 안되서 이렇게 함.
507
  def reSelectedEntry(entryList):
@@ -510,7 +619,7 @@ def setup_demo():
510
  removeEntryWorldcupBtn.click(
511
  fn=removeSelectedEntry,
512
  inputs=[entryImageUrls, selectedImageIndex],
513
- outputs=[entryListGallery, entryImageUrls],
514
  ).then(
515
  fn=reSelectedEntry,
516
  inputs=entryImageUrls,
@@ -576,6 +685,10 @@ def setup_demo():
576
  selectedImageIndex,
577
  progressBar,
578
  ],
 
 
 
 
579
  ).then(
580
  fn=lambda entryList: (
581
  gr.Info(
@@ -586,7 +699,6 @@ def setup_demo():
586
  else None
587
  ),
588
  inputs=[entryImageUrls],
589
- outputs=[],
590
  )
591
 
592
  with gr.Tab("이미지 월드컵"):
@@ -604,13 +716,35 @@ def setup_demo():
604
 
605
  with gr.Row(): # 이미지 수�� 배열.
606
  battleImage1 = gr.Image(
607
- show_label=False, scale=5, elem_classes="battle-image"
 
 
 
 
608
  )
609
  vsImage = gr.HTML(VS_IMAGE)
610
  battleImage2 = gr.Image(
611
- show_label=False, scale=5, elem_classes="battle-image"
 
 
 
 
612
  )
613
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  def winImage(winnerIndex, worldcup):
615
  print("winnerIndex:", winnerIndex)
616
  print("worldcup:", worldcup)
@@ -618,8 +752,14 @@ def setup_demo():
618
  battleTitle = worldcup.getKangRound()
619
  if finalWinner:
620
  return (
621
- gr.update(visible=winnerIndex == 0),
622
- gr.update(visible=winnerIndex == 1),
 
 
 
 
 
 
623
  worldcup,
624
  "## <center>🎉 우승 🎊</center>",
625
  gr.update(visible=False),
@@ -628,9 +768,16 @@ def setup_demo():
628
  gr.update(visible=False),
629
  )
630
  nextMatchImages = worldcup.getCurrentRoundImages()
 
631
  return (
632
- nextMatchImages[0],
633
- nextMatchImages[1],
 
 
 
 
 
 
634
  worldcup,
635
  battleTitle,
636
  gr.skip(),
@@ -649,18 +796,41 @@ def setup_demo():
649
  winImage1Btn,
650
  winImage2Btn,
651
  ]
652
- winImage1Btn.click(
653
- fn=lambda w: winImage(0, w), inputs=[worldcup], outputs=wOutputs
654
- )
655
- winImage2Btn.click(
656
- fn=lambda w: winImage(1, w), inputs=[worldcup], outputs=wOutputs
657
- )
658
- battleImage1.select(
659
- fn=lambda w: winImage(0, w), inputs=[worldcup], outputs=wOutputs
660
- )
661
- battleImage2.select(
662
- fn=lambda w: winImage(1, w), inputs=[worldcup], outputs=wOutputs
663
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
 
665
  @startWorldcupBtn.click(
666
  inputs=[entryImageUrls],
@@ -682,8 +852,16 @@ def setup_demo():
682
  twoImages = worldcup.getCurrentRoundImages()
683
  return (
684
  worldcup,
685
- gr.update(value=twoImages[0], visible=True),
686
- gr.update(value=twoImages[1], visible=True),
 
 
 
 
 
 
 
 
687
  gr.update(visible=False),
688
  gr.update(visible=True),
689
  gr.update(visible=True),
 
4
  import os
5
  import gradio as gr
6
  import replicate
7
+ import time
8
 
9
  # .env 파일 로드
10
  load_dotenv()
 
355
  .tiny-input {
356
  width: '20px',
357
  }
358
+
359
+ /***********************************************/
360
+ /* 이미지 사라지는 애니메이션 */
361
+ /***********************************************/
362
+ /* CSS for fade-out animation */
363
+ .fade-out {
364
+ animation: fadeOutAndShrink 1s forwards;
365
+ /* Add these properties to ensure smooth animation */
366
+ transform-origin: center;
367
+ display: inline-block;
368
+ }
369
+
370
+ @keyframes fadeOutAndShrink {
371
+ 0% {
372
+ opacity: 1;
373
+ transform: scale(1);
374
+ }
375
+ 100% {
376
+ opacity: 0;
377
+ transform: scale(0);
378
+ }
379
+ }
380
+
381
+ /* Optional: Add this if you want to keep the layout space after element disappears */
382
+ .fade-out.preserve-space {
383
+ visibility: hidden;
384
+ opacity: 0;
385
+ animation: fadeOutAndShrinkPreserve 1s forwards;
386
+ }
387
+
388
+ @keyframes fadeOutAndShrinkPreserve {
389
+ 0% {
390
+ opacity: 1;
391
+ transform: scale(1);
392
+ visibility: visible;
393
+ }
394
+ 99% {
395
+ transform: scale(0.01);
396
+ opacity: 0;
397
+ visibility: visible;
398
+ }
399
+ 100% {
400
+ transform: scale(0);
401
+ opacity: 0;
402
+ visibility: hidden;
403
+ }
404
+ }
405
+
406
+
407
+ /***********************************************/
408
+ /* 이미지 강조를 위해 키우는 애니메이션 */
409
+ /***********************************************/
410
+ /* CSS for fade-in animation with scale */
411
+ .fade-in {
412
+ animation: fadeInAndScale 0.3s ease forwards;
413
+ /* Add these properties to ensure smooth animation */
414
+ transform-origin: center;
415
+ display: inline-block;
416
+ opacity: 0; /* Start with opacity 0 */
417
+ }
418
+
419
+ @keyframes fadeInAndScale {
420
+ 0% {
421
+ opacity: 0;
422
+ transform: scale(1);
423
+ }
424
+ 100% {
425
+ opacity: 1;
426
+ transform: scale(1.15); /* Element grows to 1.15 times its original size */
427
+ }
428
+ }
429
+
430
+
431
+ /***********************************************/
432
+ /* 처음에는 안보이다가 점점 나타나는 애니메이션 */
433
+ /***********************************************/
434
+ .fade-in-visibility {
435
+ /* 처음에는 보이지 않음 */
436
+ opacity: 0;
437
+ /* 애니메이션 설정 */
438
+ animation: fadeInVisibility 1s ease-in forwards;
439
+ /* 애니메이션을 부드럽게 만들기 위한 설정 */
440
+ will-change: opacity;
441
+ }
442
+
443
+ @keyframes fadeInVisibility {
444
+ 0% {
445
+ opacity: 0;
446
+ }
447
+ 100% {
448
+ opacity: 1;
449
+ }
450
+ }
451
+
452
+ /***********************************************/
453
+ /* 이미지 클릭 비활성화 */
454
+ /***********************************************/
455
+ .non-clickable {
456
+ pointer-events: none; /* 모든 포인터 이벤트(클릭, 호버 등)를 비활성화 */
457
+ user-select: none; /* 텍스트 선택 방지 */
458
+ }
459
  """
460
 
461
+ BATTLE_IMAGE_INIT = "battle-image fade-in-visibility"
462
+
463
  # gr.set_static_paths(paths=["static/image/", "static/examples/"])
464
 
465
  # theme = "NoCrypt/miku" # 하늘색 테마
 
595
  interactive=False,
596
  # height=768,
597
  )
598
+ entryCount = gr.Markdown("## 이미지 월드컵 진출 이미지 수: 0")
599
 
600
  @entryListGallery.select(outputs=selectedImageIndex)
601
  def setSelectedImageIndex(selection: gr.SelectData):
 
606
  return gr.update(value=entryList), entryList
607
 
608
  del entryList[selectedIndex]
609
+ return (
610
+ entryList,
611
+ entryList,
612
+ f"## 이미지 월드컵 진출 이미지 수: {len(entryList)}",
613
+ )
614
 
615
  # 왠지 모르게 마지막 요소를 지우면 preview=True 가 안되서 이렇게 함.
616
  def reSelectedEntry(entryList):
 
619
  removeEntryWorldcupBtn.click(
620
  fn=removeSelectedEntry,
621
  inputs=[entryImageUrls, selectedImageIndex],
622
+ outputs=[entryListGallery, entryImageUrls, entryCount],
623
  ).then(
624
  fn=reSelectedEntry,
625
  inputs=entryImageUrls,
 
685
  selectedImageIndex,
686
  progressBar,
687
  ],
688
+ ).then(
689
+ fn=lambda entryImageUrls: f"## 이미지 월드컵 진출 이미지 수: {len(entryImageUrls)}",
690
+ inputs=entryImageUrls,
691
+ outputs=entryCount,
692
  ).then(
693
  fn=lambda entryList: (
694
  gr.Info(
 
699
  else None
700
  ),
701
  inputs=[entryImageUrls],
 
702
  )
703
 
704
  with gr.Tab("이미지 월드컵"):
 
716
 
717
  with gr.Row(): # 이미지 수�� 배열.
718
  battleImage1 = gr.Image(
719
+ show_label=False,
720
+ scale=5,
721
+ elem_classes="battle-image",
722
+ interactive=False,
723
+ show_download_button=False,
724
  )
725
  vsImage = gr.HTML(VS_IMAGE)
726
  battleImage2 = gr.Image(
727
+ show_label=False,
728
+ scale=5,
729
+ elem_classes="battle-image",
730
+ interactive=False,
731
+ show_download_button=False,
732
  )
733
 
734
+ def winImage0(winnerIndex):
735
+ print("winImage0 winnerIndex:", winnerIndex)
736
+ if winnerIndex == 0:
737
+ return gr.update(
738
+ elem_classes="fade-in non-clickable"
739
+ ), gr.update(elem_classes="fade-out")
740
+ else:
741
+ return gr.update(elem_classes="fade-out"), gr.update(
742
+ elem_classes="fade-in non-clickable"
743
+ )
744
+
745
+ def sleep():
746
+ time.sleep(1)
747
+
748
  def winImage(winnerIndex, worldcup):
749
  print("winnerIndex:", winnerIndex)
750
  print("worldcup:", worldcup)
 
752
  battleTitle = worldcup.getKangRound()
753
  if finalWinner:
754
  return (
755
+ gr.update(
756
+ visible=winnerIndex == 0,
757
+ elem_classes=BATTLE_IMAGE_INIT,
758
+ ),
759
+ gr.update(
760
+ visible=winnerIndex == 1,
761
+ elem_classes=BATTLE_IMAGE_INIT,
762
+ ),
763
  worldcup,
764
  "## <center>🎉 우승 🎊</center>",
765
  gr.update(visible=False),
 
768
  gr.update(visible=False),
769
  )
770
  nextMatchImages = worldcup.getCurrentRoundImages()
771
+ print("nextMatchImages:", nextMatchImages)
772
  return (
773
+ gr.update(
774
+ value=nextMatchImages[0],
775
+ elem_classes=BATTLE_IMAGE_INIT,
776
+ ),
777
+ gr.update(
778
+ value=nextMatchImages[1],
779
+ elem_classes=BATTLE_IMAGE_INIT,
780
+ ),
781
  worldcup,
782
  battleTitle,
783
  gr.skip(),
 
796
  winImage1Btn,
797
  winImage2Btn,
798
  ]
799
+
800
+ # winImage1Btn_click = winImage1Btn.click
801
+ # winImage1Btn_click(
802
+ # fn=lambda w: winImage0(0),
803
+ # inputs=[battleImage1],
804
+ # outputs=[battleImage1, battleImage2],
805
+ # ).then(fn=sleep, inputs=[], outputs=[]).then(
806
+ # fn=lambda w: winImage(0, w), inputs=[worldcup], outputs=wOutputs
807
+ # )
808
+ # winImage2Btn.click(
809
+ # fn=lambda w: winImage(1, w), inputs=[worldcup], outputs=wOutputs
810
+ # )
811
+ # battleImage1.select(
812
+ # fn=lambda w: winImage(0, w), inputs=[worldcup], outputs=wOutputs
813
+ # )
814
+ # battleImage2.select(
815
+ # fn=lambda w: winImage(1, w), inputs=[worldcup], outputs=wOutputs
816
+ # )
817
+
818
+ for event, winIndex in [
819
+ (winImage1Btn.click, 0),
820
+ (winImage2Btn.click, 1),
821
+ (battleImage1.select, 0),
822
+ (battleImage2.select, 1),
823
+ ]:
824
+ print("winIndex:", winIndex)
825
+ event(
826
+ fn=lambda w, i=winIndex: winImage0(i),
827
+ inputs=[battleImage1],
828
+ outputs=[battleImage1, battleImage2],
829
+ ).then(fn=sleep).then(
830
+ fn=lambda w, i=winIndex: winImage(i, w),
831
+ inputs=[worldcup],
832
+ outputs=wOutputs,
833
+ )
834
 
835
  @startWorldcupBtn.click(
836
  inputs=[entryImageUrls],
 
852
  twoImages = worldcup.getCurrentRoundImages()
853
  return (
854
  worldcup,
855
+ gr.update(
856
+ value=twoImages[0],
857
+ visible=True,
858
+ elem_classes=BATTLE_IMAGE_INIT,
859
+ ),
860
+ gr.update(
861
+ value=twoImages[1],
862
+ visible=True,
863
+ elem_classes=BATTLE_IMAGE_INIT,
864
+ ),
865
  gr.update(visible=False),
866
  gr.update(visible=True),
867
  gr.update(visible=True),
static/beauty/out-5.webp CHANGED