crystalwindow 5.8__py3-none-any.whl → 6.0__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.
- crystalwindow/__init__.py +2 -0
- crystalwindow/assets.py +95 -17
- crystalwindow/sprites.py +4 -0
- crystalwindow/spritesheets.py +26 -0
- crystalwindow/window.py +31 -6
- {crystalwindow-5.8.dist-info → crystalwindow-6.0.dist-info}/METADATA +2 -2
- {crystalwindow-5.8.dist-info → crystalwindow-6.0.dist-info}/RECORD +10 -9
- {crystalwindow-5.8.dist-info → crystalwindow-6.0.dist-info}/WHEEL +0 -0
- {crystalwindow-5.8.dist-info → crystalwindow-6.0.dist-info}/licenses/LICENSE +0 -0
- {crystalwindow-5.8.dist-info → crystalwindow-6.0.dist-info}/top_level.txt +0 -0
crystalwindow/__init__.py
CHANGED
crystalwindow/assets.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import random, json, pickle
|
|
3
3
|
from tkinter import PhotoImage
|
|
4
|
+
import time
|
|
4
5
|
|
|
5
6
|
try:
|
|
6
7
|
from PIL import Image, ImageTk
|
|
@@ -159,34 +160,86 @@ def flip_vertical(img):
|
|
|
159
160
|
# LOOPING ANIMATIONS / IMAGES
|
|
160
161
|
# --------------------------------------------------------
|
|
161
162
|
class LoopAnim:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
"""
|
|
164
|
+
Flexible looping animation.
|
|
165
|
+
|
|
166
|
+
Usage patterns:
|
|
167
|
+
- time-based using FPS:
|
|
168
|
+
anim = LoopAnim(frames, fps=12)
|
|
169
|
+
frame = anim.next() # auto uses time delta internally
|
|
170
|
+
- manual dt-based (e.g., game loop provides dt):
|
|
171
|
+
anim = LoopAnim(frames, fps=24)
|
|
172
|
+
frame = anim.next(dt) # dt in seconds
|
|
173
|
+
- legacy speed-based (per-call increment):
|
|
174
|
+
anim = LoopAnim(frames, speed=0.2)
|
|
175
|
+
frame = anim.next() # increments by 'speed' each call
|
|
176
|
+
|
|
177
|
+
Notes:
|
|
178
|
+
- `frames` may be any iterable (list/tuple/set); it will be converted to a list.
|
|
179
|
+
- If frames is empty, returns None from `next()`.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(self, frames, fps=None, speed=None):
|
|
183
|
+
self.frames = [f for f in frames if f is not None]
|
|
184
|
+
self.length = len(self.frames)
|
|
185
|
+
self._index = 0
|
|
186
|
+
|
|
187
|
+
# time-based mode
|
|
188
|
+
self.fps = float(fps) if fps else None
|
|
189
|
+
self.frame_duration = (1.0 / self.fps) if self.fps and self.fps > 0 else None
|
|
190
|
+
self._acc = 0.0
|
|
191
|
+
self._last_time = time.time()
|
|
192
|
+
|
|
193
|
+
# legacy speed (per-call fractional index)
|
|
194
|
+
self.speed = float(speed) if speed else None
|
|
195
|
+
self._float_index = 0.0
|
|
166
196
|
|
|
167
|
-
def next(self):
|
|
168
|
-
"""Return next frame
|
|
197
|
+
def next(self, dt=None):
|
|
198
|
+
"""Return next frame. If dt (seconds) provided, uses it; otherwise uses internal clock."""
|
|
169
199
|
if not self.frames:
|
|
170
200
|
return None
|
|
171
201
|
|
|
172
|
-
|
|
173
|
-
if self.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
202
|
+
# Time-based FPS mode (preferred)
|
|
203
|
+
if self.frame_duration:
|
|
204
|
+
if dt is None:
|
|
205
|
+
now = time.time()
|
|
206
|
+
dt = now - self._last_time
|
|
207
|
+
self._last_time = now
|
|
208
|
+
self._acc += dt
|
|
209
|
+
advance = int(self._acc / self.frame_duration)
|
|
210
|
+
if advance > 0:
|
|
211
|
+
self._acc -= advance * self.frame_duration
|
|
212
|
+
self._index = (self._index + advance) % self.length
|
|
213
|
+
return self.frames[self._index]
|
|
214
|
+
|
|
215
|
+
# Legacy speed-per-call mode
|
|
216
|
+
if self.speed:
|
|
217
|
+
self._float_index += self.speed
|
|
218
|
+
if self._float_index >= self.length:
|
|
219
|
+
self._float_index = self._float_index % self.length
|
|
220
|
+
return self.frames[int(self._float_index)]
|
|
221
|
+
|
|
222
|
+
# Default: simple step each call
|
|
223
|
+
self._index = (self._index + 1) % self.length
|
|
224
|
+
return self.frames[self._index]
|
|
177
225
|
|
|
178
226
|
|
|
179
227
|
def loop_images(*imgs, speed=0.2):
|
|
180
228
|
"""
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
229
|
+
Backwards-compatible wrapper.
|
|
230
|
+
Accepts varargs like loop_images(img1, img2, ...) or a single iterable:
|
|
231
|
+
loop_images([img1, img2], speed=0.2)
|
|
232
|
+
|
|
233
|
+
This returns a LoopAnim that uses legacy per-call `speed`.
|
|
184
234
|
"""
|
|
185
|
-
frames = [
|
|
235
|
+
frames = []
|
|
236
|
+
if len(imgs) == 1 and hasattr(imgs[0], "__iter__") and not isinstance(imgs[0], (str, bytes)):
|
|
237
|
+
frames = [x for x in imgs[0] if x is not None]
|
|
238
|
+
else:
|
|
239
|
+
frames = [x for x in imgs if x is not None]
|
|
186
240
|
|
|
187
|
-
# If nothing was passed → fallback
|
|
188
241
|
if not frames:
|
|
189
|
-
print("⚠️
|
|
242
|
+
print("⚠️ loop_images() got no frames.")
|
|
190
243
|
fb = load_image("fallback.png") if os.path.exists("fallback.png") else None
|
|
191
244
|
if fb is None:
|
|
192
245
|
return LoopAnim([None], speed=speed)
|
|
@@ -195,6 +248,31 @@ def loop_images(*imgs, speed=0.2):
|
|
|
195
248
|
return LoopAnim(frames, speed=speed)
|
|
196
249
|
|
|
197
250
|
|
|
251
|
+
def loop_imgs(*imgs, framerate=None, speed=None):
|
|
252
|
+
"""
|
|
253
|
+
New helper supporting a single iterable or varargs and an explicit framerate (FPS).
|
|
254
|
+
Usage:
|
|
255
|
+
idle = [idle1, idle2, idle3]
|
|
256
|
+
idles = loop_imgs(idle, framerate=24) # time-based, 24 FPS
|
|
257
|
+
idles = loop_imgs(idle1, idle2, framerate=12)
|
|
258
|
+
idles = loop_imgs(idle, speed=0.2) # fallback to legacy speed-per-call
|
|
259
|
+
"""
|
|
260
|
+
frames = []
|
|
261
|
+
if len(imgs) == 1 and hasattr(imgs[0], "__iter__") and not isinstance(imgs[0], (str, bytes)):
|
|
262
|
+
frames = [x for x in imgs[0] if x is not None]
|
|
263
|
+
else:
|
|
264
|
+
frames = [x for x in imgs if x is not None]
|
|
265
|
+
|
|
266
|
+
if not frames:
|
|
267
|
+
print("⚠️ loop_imgs() got no frames.")
|
|
268
|
+
fb = load_image("fallback.png") if os.path.exists("fallback.png") else None
|
|
269
|
+
if fb is None:
|
|
270
|
+
return LoopAnim([None], fps=framerate, speed=speed)
|
|
271
|
+
return LoopAnim([fb], fps=framerate, speed=speed)
|
|
272
|
+
|
|
273
|
+
return LoopAnim(frames, fps=framerate, speed=speed)
|
|
274
|
+
|
|
275
|
+
|
|
198
276
|
# --------------------------------------------------------
|
|
199
277
|
# FOLDER LOADING
|
|
200
278
|
# --------------------------------------------------------
|
crystalwindow/sprites.py
CHANGED
|
@@ -72,6 +72,10 @@ class Sprite:
|
|
|
72
72
|
"""Create sprite using a simple rectangle"""
|
|
73
73
|
return cls(pos, size=(w, h), color=color)
|
|
74
74
|
|
|
75
|
+
@classmethod
|
|
76
|
+
def group(cls, *sprites):
|
|
77
|
+
"""Create a list of sprites"""
|
|
78
|
+
return list(sprites)
|
|
75
79
|
|
|
76
80
|
# === MOVE / DRAW ===
|
|
77
81
|
def draw(self, win, cam=None):
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from PIL import Image, ImageTk
|
|
2
|
+
import xml.etree.ElementTree as ET
|
|
3
|
+
|
|
4
|
+
class SpriteSheet:
|
|
5
|
+
def __init__(self, image_path, xml_path):
|
|
6
|
+
self.pil_image = Image.open(image_path).convert("RGBA")
|
|
7
|
+
self.frames = {}
|
|
8
|
+
|
|
9
|
+
tree = ET.parse(xml_path)
|
|
10
|
+
root = tree.getroot()
|
|
11
|
+
|
|
12
|
+
for sub in root.iter("SubTexture"):
|
|
13
|
+
name = sub.attrib["name"]
|
|
14
|
+
x = int(sub.attrib["x"])
|
|
15
|
+
y = int(sub.attrib["y"])
|
|
16
|
+
w = int(sub.attrib["width"])
|
|
17
|
+
h = int(sub.attrib["height"])
|
|
18
|
+
|
|
19
|
+
self.frames[name] = (x, y, w, h)
|
|
20
|
+
|
|
21
|
+
def get_pil(self, name):
|
|
22
|
+
x, y, w, h = self.frames[name]
|
|
23
|
+
return self.pil_image.crop((x, y, x + w, y + h))
|
|
24
|
+
|
|
25
|
+
def get_tk(self, name):
|
|
26
|
+
return ImageTk.PhotoImage(self.get_pil(name))
|
crystalwindow/window.py
CHANGED
|
@@ -218,6 +218,14 @@ class Window:
|
|
|
218
218
|
|
|
219
219
|
return False
|
|
220
220
|
|
|
221
|
+
def keys_combined(self, *keys):
|
|
222
|
+
for key in keys:
|
|
223
|
+
if isinstance(key, str):
|
|
224
|
+
key = self.KEY_MAP.get(key, key)
|
|
225
|
+
if not self.keys.get(key, False):
|
|
226
|
+
return False
|
|
227
|
+
return True
|
|
228
|
+
|
|
221
229
|
def key_held(self, key):
|
|
222
230
|
if isinstance(key, str):
|
|
223
231
|
key = self.KEY_MAP.get(key, key)
|
|
@@ -533,6 +541,14 @@ class Window:
|
|
|
533
541
|
bold = kwargs.pop("bold", False)
|
|
534
542
|
italic = kwargs.pop("italic", False)
|
|
535
543
|
|
|
544
|
+
pil_font = None
|
|
545
|
+
|
|
546
|
+
if hasattr(font, "font"):
|
|
547
|
+
pil_font = font.font
|
|
548
|
+
font_name = font.font_name
|
|
549
|
+
else:
|
|
550
|
+
font_name = font
|
|
551
|
+
|
|
536
552
|
# scale
|
|
537
553
|
scaled_size = size
|
|
538
554
|
|
|
@@ -549,9 +565,9 @@ class Window:
|
|
|
549
565
|
|
|
550
566
|
# tkinter font tuple
|
|
551
567
|
if style:
|
|
552
|
-
font_tuple = (
|
|
568
|
+
font_tuple = (font_name, scaled_size, style)
|
|
553
569
|
else:
|
|
554
|
-
font_tuple = (
|
|
570
|
+
font_tuple = (font_name, scaled_size)
|
|
555
571
|
|
|
556
572
|
# ---------------- NORMAL COLOR TEXT ----------------
|
|
557
573
|
if mode != "texture":
|
|
@@ -579,10 +595,19 @@ class Window:
|
|
|
579
595
|
from PIL import Image, ImageDraw, ImageFont, ImageTk
|
|
580
596
|
|
|
581
597
|
# try load font with style
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
598
|
+
# choose correct PIL font
|
|
599
|
+
if pil_font and hasattr(font, "font_path") and font.font_path:
|
|
600
|
+
try:
|
|
601
|
+
fnt = ImageFont.truetype(font.font_path, scaled_size)
|
|
602
|
+
except:
|
|
603
|
+
fnt = ImageFont.load_default()
|
|
604
|
+
elif pil_font:
|
|
605
|
+
fnt = pil_font
|
|
606
|
+
else:
|
|
607
|
+
try:
|
|
608
|
+
fnt = ImageFont.truetype(font_name + ".ttf", scaled_size)
|
|
609
|
+
except:
|
|
610
|
+
fnt = ImageFont.load_default()
|
|
586
611
|
|
|
587
612
|
# measure text
|
|
588
613
|
temp = Image.new("L", (1, 1), 0)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: crystalwindow
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0
|
|
4
4
|
Summary: A Tkinter powered window + GUI toolkit made by Crystal (ME)! Easier apps, smoother UI and all-in-one helpers!, Gui, Buttons, FileHelper, Sprites, Animations, Colors, Math, Gravity, Camera, 3D and more!
|
|
5
5
|
Home-page: https://pypi.org/project/crystalwindow/
|
|
6
6
|
Author: CrystalBallyHereXD
|
|
7
7
|
Author-email: mavilla.519@gmail.com
|
|
8
|
-
Project-URL: Homepage, https://github.com/
|
|
8
|
+
Project-URL: Homepage, https://github.com/CrystalBallyHereXD/crystalwindow
|
|
9
9
|
Project-URL: YouTube, https://www.Youtube.com/@CrystalBallyHereXD
|
|
10
10
|
Project-URL: PiWheels, https://www.piwheels.org/project/crystalwindow/
|
|
11
11
|
Keywords: tkinter gui window toolkit easy crystalwindow crystal cw player moveable easygui python py file math gravity hex color
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
crystalwindow/FileHelper.py,sha256=U20iwND4jX1y91TOK46e8MPH8xyw7GOrZ697nPEnOPk,10706
|
|
2
2
|
crystalwindow/Fonts.py,sha256=XGqmM3GaauM5a74BtqhKk9zzKgQPcHyN3vcOP1TeoOs,954
|
|
3
3
|
crystalwindow/System.py,sha256=-ecjzVlTH4ncNnZ_SDNfvqoQLeh8Zefy9laojpQKUUk,4045
|
|
4
|
-
crystalwindow/__init__.py,sha256=
|
|
4
|
+
crystalwindow/__init__.py,sha256=C2euU6HIqgx9a6tH0FP7Rt5Uj2Je99dxYP1P5jAtkXI,3387
|
|
5
5
|
crystalwindow/ai.py,sha256=YIt6dNe1QljSAlNECCVa3DMUSIqQEJRIAAbQpYqFNNo,11525
|
|
6
6
|
crystalwindow/animation.py,sha256=zHjrdBXQeyNaLAuaGPldJueX05OZ5j31YR8NizmR0uQ,427
|
|
7
7
|
crystalwindow/apphelper.py,sha256=CAcX5n0Jq7zdBMFZkwbMt3_M0mBVVZRoXldOcXsZan4,2924
|
|
8
|
-
crystalwindow/assets.py,sha256=
|
|
8
|
+
crystalwindow/assets.py,sha256=DkTPlywZFTgjWzxsy_byt28KYU4DYfdU_FS83akh3es,14673
|
|
9
9
|
crystalwindow/camera.py,sha256=fe0DXIcZ-TSVPPHxRptFufadnkhysWRZDTJxjxf6SEo,1606
|
|
10
10
|
crystalwindow/chatvpn.py,sha256=Ij3wRNrMbPINO-SX9vx8xlrKRLvG7zJgzAN2T0jzSz8,2477
|
|
11
11
|
crystalwindow/clock.py,sha256=M7oMjnGNkFOcchqtQn_voWao4kFtxXpTJe2VUjqvGKQ,5041
|
|
@@ -24,11 +24,12 @@ crystalwindow/math.py,sha256=AGhLGkPWozNOMlSieF5u8jSDY0qakOPJIi5aWI9P91o,1598
|
|
|
24
24
|
crystalwindow/messagebus.py,sha256=9K2P_TkdQ1rt-oouIkRg_XHwMTwykttBt-9gFYaItyI,696
|
|
25
25
|
crystalwindow/objects.py,sha256=BWqjlxOXpaCGeaAocFL5L9Qh-a_MRjbN6Qb-Vd7R0K8,6262
|
|
26
26
|
crystalwindow/scores.py,sha256=O5E-rceNEaRpsC3XZ2PpE5gsLvJk86f4ChA5UNdd4b0,1069
|
|
27
|
-
crystalwindow/sprites.py,sha256=
|
|
27
|
+
crystalwindow/sprites.py,sha256=REkKkKi1oDQBjnjM7fX-49_zsJmTlwpDpoX8PlTohtQ,7599
|
|
28
|
+
crystalwindow/spritesheets.py,sha256=M_KTBAXPaM3liHf9-2rsQtXUB9GSQUkb07JS6S4-4iI,807
|
|
28
29
|
crystalwindow/tilemap.py,sha256=endJ8KcbP9EjPvL9qWsOpV4jc_Re1yH080aUyDkwufA,3378
|
|
29
30
|
crystalwindow/ver_warner.py,sha256=qEN3ulc1NixBy15FFx2R3Zu0DhyJTVJwiESGAPwpynM,3373
|
|
30
31
|
crystalwindow/websearch.py,sha256=IgsoKt27yCBHeq8yFVfSq_8sEj5KP6mqn2yNRTsRw1A,5161
|
|
31
|
-
crystalwindow/window.py,sha256=
|
|
32
|
+
crystalwindow/window.py,sha256=yo_Onk0RYEjUMhzKsNXJDBlYYFF9l9Nhbxiza3wf4UY,33128
|
|
32
33
|
crystalwindow/Icons/default_icon.png,sha256=Loq27Pxb8Wb3Sz-XwtNF1RmlLNxR4TcfOWfK-1lWcII,7724
|
|
33
34
|
crystalwindow/Icons/file_icons.png,sha256=kqjvz3gMaIbepW4XGrLZOjDYu-yhFbVxjvylS-0RO4U,5659
|
|
34
35
|
crystalwindow/gametests/3dsquare.py,sha256=MM_RRx1mz_NeBhTAoNomzn_IAbbN4ZnQZWA9VAFL8mY,821
|
|
@@ -40,8 +41,8 @@ crystalwindow/gametests/sandbox.py,sha256=Oo2tU2N0y3BPVa6T5vs_h9N6islhQrjSrr_78X
|
|
|
40
41
|
crystalwindow/gametests/squaremove.py,sha256=ei6DMnvcgpOhmxbGv-Yqmx5EqiZjKbVlZhI7YbT2hY8,643
|
|
41
42
|
crystalwindow/gametests/testtttagain.py,sha256=oIhK9MGgMVly_W2lRwD9Hn9WyPdd8JnX2HGrLTGZdxY,373
|
|
42
43
|
crystalwindow/gametests/windowtesting.py,sha256=_9X6wnV1-_X_PtNS-0zu-k209NtFIwAc4vpxLPp7V2o,97
|
|
43
|
-
crystalwindow-
|
|
44
|
-
crystalwindow-
|
|
45
|
-
crystalwindow-
|
|
46
|
-
crystalwindow-
|
|
47
|
-
crystalwindow-
|
|
44
|
+
crystalwindow-6.0.dist-info/licenses/LICENSE,sha256=Gt5cJRchdNt0guxyQMHKsATN5PM5mjuDhdO6Gzs9qQc,1096
|
|
45
|
+
crystalwindow-6.0.dist-info/METADATA,sha256=0LMK1XhmvQ2yKbO-ouU5m43xp-MFo2SsEM7VBmG_IfA,7529
|
|
46
|
+
crystalwindow-6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
47
|
+
crystalwindow-6.0.dist-info/top_level.txt,sha256=PeQSld4b19XWT-zvbYkvE2Xg8sakIMbDzSzSdOSRN8o,14
|
|
48
|
+
crystalwindow-6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|