MoreCustomTkinterWidgets 2.0.0__tar.gz → 3.0.0__tar.gz

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.
Files changed (30) hide show
  1. {MoreCustomTkinterWidgets-2.0.0/src/MoreCustomTkinterWidgets.egg-info → morecustomtkinterwidgets-3.0.0}/PKG-INFO +6 -1
  2. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/README.md +5 -0
  3. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/pyproject.toml +1 -1
  4. morecustomtkinterwidgets-3.0.0/src/MoreCustomTkinterWidgets/AnimatedImage.py +151 -0
  5. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/__init__.py +2 -1
  6. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0/src/MoreCustomTkinterWidgets.egg-info}/PKG-INFO +6 -1
  7. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/SOURCES.txt +1 -0
  8. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/LICENSE +0 -0
  9. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/setup.cfg +0 -0
  10. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/AskDialog.py +0 -0
  11. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/AskValue.py +0 -0
  12. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/BetterCTkImage.py +0 -0
  13. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/FileExplorer.py +0 -0
  14. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/back_arrow_dark.png +0 -0
  15. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/back_arrow_light.png +0 -0
  16. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/error.png +0 -0
  17. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/file_dark.png +0 -0
  18. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/file_light.png +0 -0
  19. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/folder_dark.png +0 -0
  20. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/folder_light.png +0 -0
  21. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/info.png +0 -0
  22. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/new_folder_dark.png +0 -0
  23. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/new_folder_light.png +0 -0
  24. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/warning.png +0 -0
  25. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Message.py +0 -0
  26. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Selector.py +0 -0
  27. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/SmoothFrame.py +0 -0
  28. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/dependency_links.txt +0 -0
  29. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/requires.txt +0 -0
  30. {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: MoreCustomTkinterWidgets
3
- Version: 2.0.0
3
+ Version: 3.0.0
4
4
  Summary: A package containing more widgets for customtkinter
5
5
  Author: Fastattack
6
6
  License: MIT License
@@ -57,6 +57,11 @@ It also comes with a class to ask for a file in a TopLevel (FileDialog).
57
57
 
58
58
  The FileDialog class comes with prebuilt functions to ask for files more easily: askdir, askfile
59
59
 
60
+ ### BetterCTkImage
61
+ This class is used like CTkImage, but you can round the images corners !
62
+
63
+ You can configure the radius of corner rounding by using `BetterCTkImage.configure()` and the image will update automatically everywhere it is used.
64
+
60
65
  ### Selector
61
66
  This class is used to select values in a list.
62
67
 
@@ -40,6 +40,11 @@ It also comes with a class to ask for a file in a TopLevel (FileDialog).
40
40
 
41
41
  The FileDialog class comes with prebuilt functions to ask for files more easily: askdir, askfile
42
42
 
43
+ ### BetterCTkImage
44
+ This class is used like CTkImage, but you can round the images corners !
45
+
46
+ You can configure the radius of corner rounding by using `BetterCTkImage.configure()` and the image will update automatically everywhere it is used.
47
+
43
48
  ### Selector
44
49
  This class is used to select values in a list.
45
50
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "MoreCustomTkinterWidgets"
7
- version = "2.0.0"
7
+ version = "3.0.0"
8
8
  dependencies = [
9
9
  "customtkinter>=5.2.1",
10
10
  "Pillow>=9.5.0",
@@ -0,0 +1,151 @@
1
+ import customtkinter as ctk
2
+ from PIL import Image, ImageTk
3
+ import os
4
+ from typing import Tuple
5
+
6
+
7
+ class AnimatedImage(ctk.CTkImage):
8
+ def __init__(self,
9
+ light_image: Image.Image | str = None,
10
+ dark_image: Image.Image | str = None,
11
+ size: Tuple[int, int] = (20, 20),
12
+ speed_multiplier=1.
13
+ ):
14
+ """Image object functioning like CTkImage but allows to animate Images sequences (FLI/FLC, GIF)
15
+
16
+ :param light_image: PIL.Image.Image (FLI/FLC or GIF format) or path to image for light mode
17
+ :param dark_image: PIL.Image.Image (FLI/FLC or GIF format) or path to image for dark mode
18
+ :param size: tuple (<width>, <height>) with display size for both images
19
+ :param speed_multiplier: allows to change the speed of the animation: below 1 speeds the animation up and above 1 slows the animation down
20
+ """
21
+ if type(light_image) is str:
22
+ if os.path.isfile(light_image):
23
+ light_image = Image.open(light_image)
24
+ else:
25
+ raise ValueError(f"Path to light image file is incorrect: {light_image}")
26
+ if type(dark_image) is str:
27
+ if os.path.isfile(dark_image):
28
+ dark_image = Image.open(dark_image)
29
+ else:
30
+ raise ValueError(f"Path to dark image file is incorrect: {dark_image}")
31
+
32
+ super().__init__(light_image, dark_image, size)
33
+
34
+ self._currently_animating = False
35
+ self._time_between_frames = 0
36
+ self._speed_multiplier = speed_multiplier
37
+ self.reset_after_complete = False
38
+
39
+ # New keys of the dicts self._scaled_light_photo_images and self._scaled_dark_photo_images:
40
+ # ((size_x, size_y), frame_index)
41
+
42
+ def _get_scaled_light_photo_image(self, scaled_size: Tuple[int, int]) -> "ImageTk.PhotoImage":
43
+ if (scaled_size, self._light_image.tell()) in self._scaled_light_photo_images:
44
+ return self._scaled_light_photo_images[(scaled_size, self._light_image.tell())]
45
+ else:
46
+ self._scaled_light_photo_images[(scaled_size, self._light_image.tell())] = ImageTk.PhotoImage(self._light_image.resize(scaled_size))
47
+ return self._scaled_light_photo_images[(scaled_size, self._light_image.tell())]
48
+
49
+ def _get_scaled_dark_photo_image(self, scaled_size: Tuple[int, int]) -> "ImageTk.PhotoImage":
50
+ if (scaled_size, self._dark_image.tell()) in self._scaled_dark_photo_images:
51
+ return self._scaled_dark_photo_images[(scaled_size, self._dark_image.tell())]
52
+ else:
53
+ self._scaled_dark_photo_images[(scaled_size, self._dark_image.tell())] = ImageTk.PhotoImage(self._dark_image.resize(scaled_size))
54
+ return self._scaled_dark_photo_images[(scaled_size, self._dark_image.tell())]
55
+
56
+ def configure(self, **kwargs):
57
+ if "speed_multiplier" in kwargs:
58
+ self._speed_multiplier = kwargs.pop("speed_multiplier")
59
+ time_between_2_frames = self._light_image.info["duration"] if self._light_image is not None else self._dark_image.info["duration"]
60
+ self._time_between_frames = int(time_between_2_frames * self._speed_multiplier)
61
+
62
+ super().configure(**kwargs)
63
+
64
+ def cget(self, attribute_name: str) -> any:
65
+ if "speed_multiplier" == attribute_name:
66
+ return self._speed_multiplier
67
+ else:
68
+ return super().cget(attribute_name)
69
+
70
+ def _next_frame(self):
71
+ """ Internal func: changes the current frame to the next one"""
72
+ if self._currently_animating:
73
+ try: # light image
74
+ if self._light_image is not None:
75
+ self._light_image.seek(self._light_image.tell() + 1)
76
+ except EOFError:
77
+ self._light_image.seek(0)
78
+
79
+ try: # dark image
80
+ if self._dark_image is not None:
81
+ self._dark_image.seek(self._dark_image.tell() + 1)
82
+ except EOFError:
83
+ self._dark_image.seek(0)
84
+
85
+ for callback in self._configure_callback_list:
86
+ callback()
87
+
88
+ self._configure_callback_list[0].__self__.after(self._time_between_frames, self._next_frame)
89
+
90
+ def get_animation_state(self) -> bool:
91
+ """Returns information about the animation
92
+
93
+ :return: True if the animation is on over, False otherwise
94
+ """
95
+ return self._currently_animating
96
+
97
+ def start_animation(self):
98
+ """ Starts the animation loop """
99
+ self._currently_animating = True
100
+ time_between_2_frames = self._light_image.info["duration"] if self._light_image is not None else self._dark_image.info["duration"]
101
+ self._time_between_frames = int(time_between_2_frames * self._speed_multiplier)
102
+ self._next_frame()
103
+
104
+ def stop_animation(self):
105
+ """ Stops the animation loop """
106
+ if self._currently_animating:
107
+ self._currently_animating = False
108
+ if self.reset_after_complete:
109
+ self.set_to_frame(0)
110
+ self.reset_after_complete = False
111
+ else:
112
+ raise RuntimeError("Tried to stop the animation but it was not already running")
113
+
114
+ def start_animation_for(self, ms: int, reset_after_complete=False):
115
+ """Starts the animation and stops automatically after the given time
116
+
117
+ :param ms: time to run the animation for (in ms)
118
+ :param reset_after_complete: if set to True: resets to the first frame of the animation when the animation stops
119
+ """
120
+ try:
121
+ self._configure_callback_list[0].__self__.after(ms, self.stop_animation)
122
+ except AttributeError:
123
+ pass
124
+ else:
125
+ self.start_animation()
126
+ self.reset_after_complete = reset_after_complete
127
+
128
+ def set_to_frame(self, frame_index: int):
129
+ """Sets the animation to the given frame
130
+
131
+ :param frame_index: frame index to set the animation to (1st frame is index 0)
132
+ """
133
+ if self._light_image is not None:
134
+ self._light_image.seek(frame_index)
135
+ if self._dark_image is not None:
136
+ self._dark_image.seek(frame_index)
137
+ for callback in self._configure_callback_list:
138
+ callback()
139
+
140
+
141
+ win = ctk.CTk()
142
+ win.title("MoreCustomTkinterWidgets examples !")
143
+ win.geometry("400x300")
144
+
145
+ image = AnimatedImage(r"C:\Users\fastattack\Desktop\loading.gif", size=(100, 100))
146
+ label = ctk.CTkLabel(win, text="", image=image)
147
+ label.pack()
148
+
149
+ image.start_animation()
150
+
151
+ win.mainloop()
@@ -5,6 +5,7 @@ from .FileExplorer import FileExplorer, Filedialog, askfile, askdir
5
5
  from .Selector import Selector
6
6
  from .SmoothFrame import SmoothFrame, get_coordinates_from_grid
7
7
  from .BetterCTkImage import BetterCTkImage
8
+ from .AnimatedImage import AnimatedImage
8
9
 
9
10
 
10
- _version = "2.0.0"
11
+ _version = "3.0.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: MoreCustomTkinterWidgets
3
- Version: 2.0.0
3
+ Version: 3.0.0
4
4
  Summary: A package containing more widgets for customtkinter
5
5
  Author: Fastattack
6
6
  License: MIT License
@@ -57,6 +57,11 @@ It also comes with a class to ask for a file in a TopLevel (FileDialog).
57
57
 
58
58
  The FileDialog class comes with prebuilt functions to ask for files more easily: askdir, askfile
59
59
 
60
+ ### BetterCTkImage
61
+ This class is used like CTkImage, but you can round the images corners !
62
+
63
+ You can configure the radius of corner rounding by using `BetterCTkImage.configure()` and the image will update automatically everywhere it is used.
64
+
60
65
  ### Selector
61
66
  This class is used to select values in a list.
62
67
 
@@ -1,6 +1,7 @@
1
1
  LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
+ src/MoreCustomTkinterWidgets/AnimatedImage.py
4
5
  src/MoreCustomTkinterWidgets/AskDialog.py
5
6
  src/MoreCustomTkinterWidgets/AskValue.py
6
7
  src/MoreCustomTkinterWidgets/BetterCTkImage.py