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.
- {MoreCustomTkinterWidgets-2.0.0/src/MoreCustomTkinterWidgets.egg-info → morecustomtkinterwidgets-3.0.0}/PKG-INFO +6 -1
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/README.md +5 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/pyproject.toml +1 -1
- morecustomtkinterwidgets-3.0.0/src/MoreCustomTkinterWidgets/AnimatedImage.py +151 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/__init__.py +2 -1
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0/src/MoreCustomTkinterWidgets.egg-info}/PKG-INFO +6 -1
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/SOURCES.txt +1 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/LICENSE +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/setup.cfg +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/AskDialog.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/AskValue.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/BetterCTkImage.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/FileExplorer.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/back_arrow_dark.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/back_arrow_light.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/error.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/file_dark.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/file_light.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/folder_dark.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/folder_light.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/info.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/new_folder_dark.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/new_folder_light.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Images/warning.png +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Message.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/Selector.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets/SmoothFrame.py +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/dependency_links.txt +0 -0
- {MoreCustomTkinterWidgets-2.0.0 → morecustomtkinterwidgets-3.0.0}/src/MoreCustomTkinterWidgets.egg-info/requires.txt +0 -0
- {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:
|
|
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
|
|
|
@@ -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 = "
|
|
11
|
+
_version = "3.0.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: MoreCustomTkinterWidgets
|
|
3
|
-
Version:
|
|
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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|