crystalwindow 3.7.1b1__py3-none-any.whl → 3.8.1__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 CHANGED
@@ -12,7 +12,15 @@ from .FileHelper import FileHelper
12
12
  from .math import Mathematics
13
13
 
14
14
  # === Assets & Animation ===
15
- from .assets import load_image, load_folder_images, load_music, play_music
15
+ from .assets import (
16
+ load_image,
17
+ load_folder_images,
18
+ load_music,
19
+ play_music,
20
+ flip_image,
21
+ flip_horizontal,
22
+ flip_vertical,
23
+ )
16
24
  from .animation import Animation
17
25
 
18
26
  # === Collision ===
@@ -23,7 +31,7 @@ from .gui import Button, Label, GUIManager, random_color, hex_to_rgb, Fade
23
31
  from .gui_ext import Toggle, Slider
24
32
 
25
33
  # === Time ===
26
- from .clock import Clock # ⏰ for frame timing / tick control
34
+ from .clock import Clock
27
35
 
28
36
  # === Drawing Helpers ===
29
37
  from .draw_helpers import gradient_rect, CameraShake
@@ -36,14 +44,22 @@ from .fun_helpers import random_name, DebugOverlay
36
44
  from .camera import Camera
37
45
 
38
46
  # === 3D Engine ===
39
- from .crystal3d import CW3D # 🧊 brand new 3D handler!
47
+ from .crystal3d import CW3D
48
+
40
49
 
41
50
  __all__ = [
42
51
  # --- Core ---
43
52
  "Window", "Sprite", "TileMap", "Player", "Gravity", "FileHelper", "Mathematics",
44
53
 
45
54
  # --- Assets & Animation ---
46
- "load_image", "load_folder_images", "load_music", "play_music", "Animation",
55
+ "load_image",
56
+ "load_folder_images",
57
+ "load_music",
58
+ "play_music",
59
+ "flip_image",
60
+ "flip_horizontal",
61
+ "flip_vertical",
62
+ "Animation",
47
63
 
48
64
  # --- Collision ---
49
65
  "check_collision", "resolve_collision",
@@ -64,5 +80,5 @@ __all__ = [
64
80
  "random_name", "DebugOverlay", "Camera",
65
81
 
66
82
  # --- 3D ---
67
- "CW3D"
83
+ "CW3D",
68
84
  ]
crystalwindow/assets.py CHANGED
@@ -1,28 +1,79 @@
1
+ # assets.py
1
2
  import os
2
3
  import random
3
4
  from tkinter import PhotoImage
4
5
 
5
6
  try:
6
- from PIL import Image
7
- except:
7
+ from PIL import Image, ImageTk
8
+ except ImportError:
8
9
  Image = None
10
+ ImageTk = None
9
11
 
10
- ASSETS = {}
12
+ ASSETS = {} # cache for all loaded images
11
13
 
12
- def load_image(path):
13
- # try normal load
14
- if os.path.exists(path):
14
+
15
+ # --------------------------------------------------------
16
+ # MAIN IMAGE LOADER
17
+ # --------------------------------------------------------
18
+ def load_image(path, flip_h=False, flip_v=False):
19
+ """
20
+ Loads an image and returns a Tk PhotoImage.
21
+ Supports flipping using Pillow.
22
+ """
23
+
24
+ key = f"{path}|h={flip_h}|v={flip_v}"
25
+ if key in ASSETS:
26
+ return ASSETS[key]
27
+
28
+ if not os.path.exists(path):
29
+ print(f"⚠️ Missing file: {path}")
30
+ img = generate_fallback(path)
31
+ ASSETS[key] = img
32
+ return img
33
+
34
+ # If Pillow is missing → load normal image
35
+ if Image is None or ImageTk is None:
15
36
  try:
16
37
  img = PhotoImage(file=path)
17
- ASSETS[path] = img
38
+ ASSETS[key] = img
18
39
  return img
19
40
  except:
20
- print(f"⚠️ Ops!, could not load img: {path}")
21
- else:
22
- print(f"⚠️ Ops!, file not found: {path}")
41
+ fb = generate_fallback(path)
42
+ ASSETS[key] = fb
43
+ return fb
44
+
45
+ # Load with PIL to allow flipping
46
+ try:
47
+ pil_img = Image.open(path)
48
+
49
+ if flip_h:
50
+ pil_img = pil_img.transpose(Image.FLIP_LEFT_RIGHT)
51
+ if flip_v:
52
+ pil_img = pil_img.transpose(Image.FLIP_TOP_BOTTOM)
53
+
54
+ tk_img = ImageTk.PhotoImage(pil_img)
55
+ ASSETS[key] = tk_img
56
+ return tk_img
57
+
58
+ except Exception as e:
59
+ print(f"⚠️ Error loading {path}: {e}")
60
+ fb = generate_fallback(path)
61
+ ASSETS[key] = fb
62
+ return fb
63
+
64
+
65
+ # --------------------------------------------------------
66
+ # FALLBACK NODE
67
+ # --------------------------------------------------------
68
+ def generate_fallback(path):
69
+ """Fallback colored block used when an image is missing."""
70
+ rand_color = (
71
+ random.randint(50, 255),
72
+ random.randint(50, 255),
73
+ random.randint(50, 255),
74
+ )
23
75
 
24
- # fallback: get natural size using PIL if possible
25
- if Image is not None:
76
+ if Image:
26
77
  try:
27
78
  pil_img = Image.open(path)
28
79
  w, h = pil_img.size
@@ -31,13 +82,6 @@ def load_image(path):
31
82
  else:
32
83
  w, h = 64, 64
33
84
 
34
- # RANDOM COLOR FALLBACK (chaos energy)
35
- rand_color = (
36
- random.randint(0, 255),
37
- random.randint(0, 255),
38
- random.randint(0, 255)
39
- )
40
-
41
85
  return {
42
86
  "fallback": True,
43
87
  "size": (w, h),
@@ -45,28 +89,62 @@ def load_image(path):
45
89
  }
46
90
 
47
91
 
92
+ # --------------------------------------------------------
93
+ # PYGAME-STYLE FLIP HELPERS
94
+ # --------------------------------------------------------
95
+ def flip_image(img, flip_h=False, flip_v=False):
96
+ """
97
+ Returns a NEW flipped PhotoImage.
98
+ Like pygame.transform.flip().
99
+ """
100
+ if Image is None or ImageTk is None:
101
+ print("⚠️ Pillow not installed; cannot flip images.")
102
+ return img
103
+
104
+ pil_img = ImageTk.getimage(img)
105
+
106
+ if flip_h:
107
+ pil_img = pil_img.transpose(Image.FLIP_LEFT_RIGHT)
108
+ if flip_v:
109
+ pil_img = pil_img.transpose(Image.FLIP_TOP_BOTTOM)
110
+
111
+ return ImageTk.PhotoImage(pil_img)
112
+
113
+
114
+ def flip_horizontal(img):
115
+ return flip_image(img, flip_h=True)
116
+
117
+
118
+ def flip_vertical(img):
119
+ return flip_image(img, flip_v=True)
120
+
121
+
122
+ # --------------------------------------------------------
123
+ # FOLDER LOADING
124
+ # --------------------------------------------------------
48
125
  def load_folder_images(folder, nested=True):
49
126
  if not os.path.exists(folder):
50
- print(f"⚠️ Ops!, folder not found: {folder}")
127
+ print(f"⚠️ Folder not found: {folder}")
51
128
  return {}
52
129
 
53
130
  result = {}
54
131
  for item in os.listdir(folder):
55
- full_path = os.path.join(folder, item)
132
+ full = os.path.join(folder, item)
56
133
 
57
- if os.path.isdir(full_path) and nested:
58
- result[item] = load_folder_images(full_path, nested=True)
134
+ if os.path.isdir(full) and nested:
135
+ result[item] = load_folder_images(full)
59
136
 
60
137
  elif item.lower().endswith((".png", ".gif")):
61
- result[item] = load_image(full_path)
138
+ result[item] = load_image(full)
62
139
 
63
140
  return result
64
141
 
65
142
 
143
+ # --------------------------------------------------------
144
+ # MUSIC PLACEHOLDER
145
+ # --------------------------------------------------------
66
146
  def load_music(path):
67
- print(f"[assets] Music loading not supported in this crystalwindow ver, sorry! ~Crystal: {path}")
68
- return None
69
-
147
+ print(f"[assets] Music load not supported: {path}")
70
148
 
71
149
  def play_music(loop=-1):
72
- print("[assets] Music playback not supported in this ver sorry! ~Crystal")
150
+ print("[assets] Music playback not supported.")
crystalwindow/sprites.py CHANGED
@@ -1,50 +1,58 @@
1
- import random
1
+ # sprites.py
2
2
  from tkinter import PhotoImage
3
+ from PIL import Image, ImageTk
4
+
3
5
 
4
6
  class Sprite:
5
7
  def __init__(self, pos, size=None, image=None, color=(255, 0, 0)):
6
- """
7
- pos: (x, y)
8
- size: (w, h) optional
9
- image: PhotoImage (Tkinter)
10
- color: fill color if no image
11
- """
12
- self.pos = pos
13
8
  self.x, self.y = pos
14
- self.image = image
9
+ self.pos = pos
10
+
11
+ self.image = None
15
12
  self.color = color
16
13
 
17
- if image is not None:
18
- self.width = image.width()
19
- self.height = image.height()
14
+ # If fallback dict provided from assets
15
+ if isinstance(image, dict) and image.get("fallback"):
16
+ self.width, self.height = image["size"]
17
+ self.color = image["color"]
18
+
19
+ # Normal image
20
+ elif isinstance(image, PhotoImage):
21
+ self.set_image(image)
22
+
23
+ # Rectangle sprite
20
24
  elif size is not None:
21
25
  self.width, self.height = size
26
+
22
27
  else:
23
- raise ValueError("Sprite needs either 'size' or 'image'")
28
+ raise ValueError("Sprite needs 'size' or 'image'")
24
29
 
25
- # optional velocity fields for physics
30
+ # Movement physics
26
31
  self.vel_x = 0
27
32
  self.vel_y = 0
28
33
 
29
- # === CLASS METHODS ===
34
+ # Optional direction flag
35
+ self.facing_left = False
36
+
37
+ # --------------------------------------
38
+ # Constructors
39
+ # --------------------------------------
30
40
  @classmethod
31
41
  def image(cls, img, pos):
32
- # fallback if img is missing
33
42
  if isinstance(img, dict) and img.get("fallback"):
34
- print("⚠️ Missing image, making RANDOM rect fallback.")
35
43
  w, h = img["size"]
36
44
  color = img["color"]
37
- return cls.rect(pos, w, h, color=color)
45
+ return cls(pos, size=(w, h), color=color)
38
46
  return cls(pos, image=img)
39
47
 
40
48
  @classmethod
41
49
  def rect(cls, pos, w, h, color=(255, 0, 0)):
42
- """Create sprite using a plain colored rectangle"""
43
50
  return cls(pos, size=(w, h), color=color)
44
51
 
45
- # === METHODS ===
52
+ # --------------------------------------
53
+ # Draw
54
+ # --------------------------------------
46
55
  def draw(self, win, cam=None):
47
- """Draw sprite on a CrystalWindow or Tkinter canvas"""
48
56
  if cam:
49
57
  draw_x, draw_y = cam.apply(self)
50
58
  else:
@@ -55,24 +63,53 @@ class Sprite:
55
63
  else:
56
64
  win.draw_rect(self.color, (draw_x, draw_y, self.width, self.height))
57
65
 
66
+ # --------------------------------------
67
+ # Movement
68
+ # --------------------------------------
58
69
  def move(self, dx, dy):
59
- """Move sprite manually by dx/dy"""
60
70
  self.x += dx
61
71
  self.y += dy
62
72
  self.pos = (self.x, self.y)
63
73
 
64
74
  def apply_velocity(self, dt=1):
65
- """Apply vel_x/vel_y to x/y for physics"""
66
75
  self.x += self.vel_x * dt
67
76
  self.y += self.vel_y * dt
68
77
  self.pos = (self.x, self.y)
69
78
 
70
- # === COLLISION ===
79
+ # --------------------------------------
80
+ # Image setter
81
+ # --------------------------------------
82
+ def set_image(self, new_image):
83
+ """Set sprite image and update size."""
84
+ self.image = new_image
85
+ self.width = new_image.width()
86
+ self.height = new_image.height()
87
+
88
+ # --------------------------------------
89
+ # In-place flipping (optional)
90
+ # --------------------------------------
91
+ def flip_horizontal(self):
92
+ if not self.image:
93
+ return
94
+ pil = ImageTk.getimage(self.image)
95
+ flipped = pil.transpose(Image.FLIP_LEFT_RIGHT)
96
+ self.set_image(ImageTk.PhotoImage(flipped))
97
+ self.facing_left = not self.facing_left
98
+
99
+ def flip_vertical(self):
100
+ if not self.image:
101
+ return
102
+ pil = ImageTk.getimage(self.image)
103
+ flipped = pil.transpose(Image.FLIP_TOP_BOTTOM)
104
+ self.set_image(ImageTk.PhotoImage(flipped))
105
+
106
+ # --------------------------------------
107
+ # Collision
108
+ # --------------------------------------
71
109
  def colliderect(self, other):
72
- """Check if self collides with another sprite"""
73
110
  return (
74
- self.x < other.x + getattr(other, "width", 0) and
75
- self.x + getattr(self, "width", 0) > other.x and
76
- self.y < other.y + getattr(other, "height", 0) and
77
- self.y + getattr(self, "height", 0) > other.y
111
+ self.x < other.x + other.width and
112
+ self.x + self.width > other.x and
113
+ self.y < other.y + other.height and
114
+ self.y + self.height > other.y
78
115
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crystalwindow
3
- Version: 3.7.1b1
3
+ Version: 3.8.1
4
4
  Summary: A Tkinter powered window + GUI toolkit made by Crystal (MEEEEEE)! Easier apps, smoother UI and all-in-one helpers!
5
5
  Home-page: https://pypi.org/project/crystalwindow/
6
6
  Author: CrystalBallyHereXD
@@ -1,7 +1,7 @@
1
1
  crystalwindow/FileHelper.py,sha256=aUnnRG7UwvzJt-idjWjmpwy3RM6nqLlC3-7Bae6Yb94,5471
2
- crystalwindow/__init__.py,sha256=SAXfSxPb-In3ZlYDQ0itMIJGDWSexBmqO39Kkh-FDtU,1911
2
+ crystalwindow/__init__.py,sha256=1O0qUvD6dGRZEwCXZU5byqWQUdRNYhyUZ54SN_NM2Dk,2015
3
3
  crystalwindow/animation.py,sha256=zHjrdBXQeyNaLAuaGPldJueX05OZ5j31YR8NizmR0uQ,427
4
- crystalwindow/assets.py,sha256=X99Laop5BREE4y7MGaMbWJLaz-q_HGNgNLcXe1yNMdY,1770
4
+ crystalwindow/assets.py,sha256=3FubtFHcemIFBiV5h79QyvKkHiLSUNBEPbGP4UyGRyI,3999
5
5
  crystalwindow/camera.py,sha256=tbn4X-jxMIszAUg3Iu-89gJN5nij0mjPMEzGotcLbJI,712
6
6
  crystalwindow/clock.py,sha256=iryeURnfXx6TIDyJhpxe0KkeSaHCdxrMckN6Tby00i0,461
7
7
  crystalwindow/collision.py,sha256=hpkHTp_KparghVK-itp_rxuYdd2GbQMxICHlUBv0rSw,472
@@ -16,7 +16,7 @@ crystalwindow/gui.py,sha256=D-El18XUaZQR-XZoOr28hnhO7EEoGuypCHxPbPR_yng,3680
16
16
  crystalwindow/gui_ext.py,sha256=ZhNgc5eK6Vzj-jUNeFzimuEbLnTmM7Bx594Z34_N0oM,2856
17
17
  crystalwindow/math.py,sha256=slOY3KQZ99VDMUMxsSJabMyRtJcwVzEyxR2RoXspHho,963
18
18
  crystalwindow/player.py,sha256=4wpIdUZLTlRXV8fDRQ11yVJRbx_cv8Ekpn2y7pQGgAQ,3442
19
- crystalwindow/sprites.py,sha256=YVoLnpLsCHlHmTahk8hwWnUtkcI-FC_w4OjZN4YtmLs,2569
19
+ crystalwindow/sprites.py,sha256=b_ErcbqoWduk4otxcETJhNsSNfpSUMmcjV4qL5gMf5g,3590
20
20
  crystalwindow/tilemap.py,sha256=PHoKL1eWuNeHIf0w-Jh5MGdQGEgklVsxqqJOS-GNMKI,322
21
21
  crystalwindow/ver_warner.py,sha256=qEN3ulc1NixBy15FFx2R3Zu0DhyJTVJwiESGAPwpynM,3373
22
22
  crystalwindow/window.py,sha256=e6C8yIdVdjOduIeU0ZmlrNn8GmyvHuzKs473pRggkz0,18180
@@ -31,8 +31,8 @@ crystalwindow/gametests/guitesting.py,sha256=SrOssY5peCQEV6TQ1AiOWtjb9phVGdRzW-Q
31
31
  crystalwindow/gametests/sandbox.py,sha256=Oo2tU2N0y3BPVa6T5vs_h9N6islhQrjSrr_78XLut5I,1007
32
32
  crystalwindow/gametests/squaremove.py,sha256=poP2Zjl2oc2HVvIAgIK34H2jVj6otL4jEdvAOR6L9sI,572
33
33
  crystalwindow/gametests/windowtesting.py,sha256=_9X6wnV1-_X_PtNS-0zu-k209NtFIwAc4vpxLPp7V2o,97
34
- crystalwindow-3.7.1b1.dist-info/licenses/LICENSE,sha256=7pvUgvRXL3ED5VVAZPH5oGn1CaIXcyoDC9gqox6sB0g,1079
35
- crystalwindow-3.7.1b1.dist-info/METADATA,sha256=nI18W8lMDTpZBJiMOjQhUKOq1S8LQ-S0dUi_PuaCAA0,7342
36
- crystalwindow-3.7.1b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- crystalwindow-3.7.1b1.dist-info/top_level.txt,sha256=PeQSld4b19XWT-zvbYkvE2Xg8sakIMbDzSzSdOSRN8o,14
38
- crystalwindow-3.7.1b1.dist-info/RECORD,,
34
+ crystalwindow-3.8.1.dist-info/licenses/LICENSE,sha256=7pvUgvRXL3ED5VVAZPH5oGn1CaIXcyoDC9gqox6sB0g,1079
35
+ crystalwindow-3.8.1.dist-info/METADATA,sha256=L7kncNlmZMHmJl0AeneiHfGYxt7UkJiYtBKeRXbVkhA,7340
36
+ crystalwindow-3.8.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ crystalwindow-3.8.1.dist-info/top_level.txt,sha256=PeQSld4b19XWT-zvbYkvE2Xg8sakIMbDzSzSdOSRN8o,14
38
+ crystalwindow-3.8.1.dist-info/RECORD,,