-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuxElements.py
725 lines (613 loc) · 29.8 KB
/
uxElements.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
import pyglet
import os
from pyglet.image import ImageData
import numpy as np
################################################################################################
# INFORMATION
#
# File Name : uxElements.py
# Developer : Rishi Balasubramanian
# Call Sign : RBGA
# First Stable Build : 11th AUG 2024
# Use Case : Custom UI Elements Class.
#
# Type : Classes
# Inputs : Pos X (value), Pos Y (value), Button Width (value),
# Button Height (value), videos (list), Batch (Pyglet Object),
# Group (opt), Verbose = True
#
# Output : Ux Objects
# Description : The UX elements consists of classes for
# uxLiveButton and uxWindowElements.
# ------------------------------------------------------------------
# LAST MODIFICATION
# Developer : Rishi Balasubramanian
# Call Sign : RBGA
# Date of Modification : 11th AUG 2024
#
# Description : Added Information Block and Code Module
# Block for every Code Module in the file.
#------------------------------------------------------------------
#
################################################################################################
class uxWindowElements(pyglet.event.EventDispatcher):
###----------------------------------------------------------------------
# <__init__>()
# Inputs :
# - `x`, `y`: Initial position of the window element.
# - `width`, `height`: Initial dimensions of the window element.
# - `WINDOW_WIDTH`, `WINDOW_HEIGHT`: The dimensions of the entire window.
# - `entrance_videos`, `loop_videos`: Paths to the video folders for entrance and loop states.
# - `batch`: Pyglet batch to which the element belongs.
# - `group`: Pyglet group for layering (optional).
# - `verbose`: Flag for enabling verbose logging.
# Output : None
# Description :
# Initializes the window element, loads the videos for different states,
# sets the initial state, and resizes the element based on the window size.
###----------------------------------------------------------------------
def __init__(self, x, y, width, height, WINDOW_WIDTH, WINDOW_HEIGHT, entrance_videos, loop_videos, batch, group=None, verbose=True):
self.x = x
self.y = y
self.original_width = width
self.original_height = height
self.window_width = width
self.window_height = height
self.batch = batch
self.group = group if group is not None else pyglet.graphics.Group(order=0)
self.entrance_videos = entrance_videos
self.loop_videos = loop_videos
self.state = 'entrance'
self.current_video = None
self.verbose = verbose
self.sprite = None
# Store initial window size as reference
self.initial_window_width = WINDOW_WIDTH
self.initial_window_height = WINDOW_HEIGHT
self.load_videos()
self.set_state(self.state)
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
###----------------------------------------------------------------------
# <resize>()
# Inputs :
# - `window_width`, `window_height`: The new dimensions of the window.
# Output : None
# Description :
# Resizes the window element proportionally based on the new window dimensions
# while maintaining the aspect ratio. Updates the sprite's scale accordingly.
###----------------------------------------------------------------------
def resize(self, window_width, window_height):
width_factor = window_width / self.initial_window_width
height_factor = window_height / self.initial_window_height
new_width = int(self.original_width * width_factor)
new_height = int(self.original_height * height_factor)
aspect_ratio = self.original_width / self.original_height
if new_width / new_height > aspect_ratio:
new_width = int(new_height * aspect_ratio)
else:
new_height = int(new_width / aspect_ratio)
if self.window_width != new_width or self.window_height != new_height:
self.window_width = new_width
self.window_height = new_height
if self.verbose:
print(f"Window frame resized to {self.window_width}x{self.window_height}")
if self.verbose:
print(f"Resize called with window_width={window_width}, window_height={window_height}")
# Update sprite with new dimensions
if self.sprite:
self.sprite.scale = min(self.window_width / self.sprite.width, self.window_height / self.sprite.height)
###----------------------------------------------------------------------
# <load_videos>()
# Inputs : None
# Output : None
# Description :
# Loads the images from the provided video folders for both the entrance
# and loop states and stores them for future use.
###----------------------------------------------------------------------
def load_videos(self):
self.video_captures = {
'entrance': self.load_images(self.entrance_videos),
'loop': self.load_images(self.loop_videos)
}
if self.verbose:
print("Videos loaded for entrance and loop states")
###----------------------------------------------------------------------
# <load_images>()
# Inputs :
# - `path`: The path to the folder containing the image sequence.
# Output :
# - A list of loaded Pyglet images.
# Description :
# Loads all PNG images from the specified folder, sorts them, and returns
# them as a list of Pyglet image objects.
###----------------------------------------------------------------------
def load_images(self, path):
image_folder = f'UiElements/{path}'
image_files = sorted([f for f in os.listdir(image_folder) if f.endswith('.png')])
images = [pyglet.image.load(os.path.join(image_folder, f)) for f in image_files]
return images
###----------------------------------------------------------------------
# <set_state>()
# Inputs :
# - `new_state`: The state to transition to (e.g., 'entrance', 'loop').
# Output : None
# Description :
# Switches the window element to the specified state, updating the current
# video to the corresponding image sequence. Determines whether the video
# should loop based on the state.
###----------------------------------------------------------------------
def set_state(self, new_state):
if new_state in self.video_captures:
if self.verbose:
print(f"Switching to {new_state}")
self.state = new_state
self.current_video = self.video_captures[new_state]
self.current_frame = 0
self.loop = new_state == 'loop'
###----------------------------------------------------------------------
# <on_eos>()
# Inputs : None
# Output : None
# Description :
# Handles the end of a video stream (EOS). If the current state is 'entrance',
# it automatically transitions to the 'loop' state.
###----------------------------------------------------------------------
def on_eos(self):
if self.verbose:
print(f"End of stream for state {self.state}")
if self.state == 'entrance':
self.set_state('loop')
###----------------------------------------------------------------------
# <update_sprite>()
# Inputs : None
# Output : None
# Description :
# Updates the sprite to display the next frame in the current video sequence.
# Handles end-of-sequence logic and scales the sprite according to the window size.
###----------------------------------------------------------------------
def update_sprite(self):
if self.current_video:
# Increment the index and handle end of sequence (EOS)
self.current_frame += 1
if self.current_frame >= len(self.current_video):
self.current_frame = 0
if not self.loop:
self.on_eos()
return
# Get the current image data
image_data = self.current_video[self.current_frame]
if self.sprite:
self.sprite.delete()
self.sprite = pyglet.sprite.Sprite(image_data, x=self.x, y=self.y, batch=self.batch, group=self.group)
self.sprite.scale = min(self.window_width / image_data.width, self.window_height / image_data.height)
if self.verbose:
print(f"Sprite updated for {self.state}. Index: {self.current_frame}")
###----------------------------------------------------------------------
# <draw>()
# Inputs : None
# Output : None
# Description :
# Draws the current sprite on the screen, updating its position and rendering it
# within the window element.
###----------------------------------------------------------------------
def draw(self):
if self.sprite:
self.sprite.update(x=self.x, y=self.y)
self.sprite.draw()
###----------------------------------------------------------------------
# <update>()
# Inputs :
# - `dt`: The time delta since the last update.
# Output : None
# Description :
# Periodically updates the window element, particularly the sprite, by
# advancing to the next frame in the video sequence.
###----------------------------------------------------------------------
def update(self, dt):
self.update_sprite()
class uxLiveButton(pyglet.event.EventDispatcher):
###----------------------------------------------------------------------
# <__init__>()
# Inputs :
# - `x`, `y`: Initial position of the button.
# - `width`, `height`: Initial dimensions of the button.
# - `WINDOW_WIDTH`, `WINDOW_HEIGHT`: The dimensions of the entire window.
# - `videos`: A dictionary mapping states to video folders.
# - `batch`: Pyglet batch to which the button belongs.
# - `group`: Pyglet group for layering (optional).
# - `on_toggle_callback`: Callback function to execute when the button is toggled (optional).
# - `verbose`: Flag for enabling verbose logging.
# Output : None
# Description :
# Initializes the live button, loads the videos for different states,
# sets the initial state, and resizes the button based on the window size.
###----------------------------------------------------------------------
def __init__(self, x, y, width, height, WINDOW_WIDTH, WINDOW_HEIGHT, videos, batch, group=None, on_toggle_callback=None, verbose=True):
self.x = x
self.y = y
self.original_width = width
self.original_height = height
self.button_width = width
self.button_height = height
self.batch = batch
self.group = group if group is not None else pyglet.graphics.Group(order=1)
self.videos = videos
self.state = 'idle'
self.current_video = None
self.verbose = verbose
self.on_toggle_callback = on_toggle_callback
self.sprite = None
# Store initial window size as reference
self.initial_window_width = WINDOW_WIDTH
self.initial_window_height = WINDOW_HEIGHT
self.load_videos()
self.set_state(self.state)
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
###----------------------------------------------------------------------
# <resize>()
# Inputs :
# - `window_width`, `window_height`: The new dimensions of the window.
# Output : None
# Description :
# Resizes the button proportionally based on the new window dimensions
# while maintaining the aspect ratio. Updates the sprite's scale accordingly.
###----------------------------------------------------------------------
def resize(self, window_width, window_height):
width_factor = window_width / self.initial_window_width
height_factor = window_height / self.initial_window_height
new_width = int(self.original_width * width_factor)
new_height = int(self.original_height * height_factor)
aspect_ratio = self.original_width / self.original_height
if new_width / new_height > aspect_ratio:
new_width = int(new_height * aspect_ratio)
else:
new_height = int(new_width / aspect_ratio)
if self.button_width != new_width or self.button_height != new_height:
self.button_width = new_width
self.button_height = new_height
if self.verbose:
print(f"Button resized to {self.button_width}x{self.button_height}")
if self.verbose:
print(f"Resize called with window_width={window_width}, window_height={window_height}")
# Update sprite with new dimensions
if self.sprite:
self.sprite.scale = min(self.button_width / self.sprite.width, self.button_height / self.sprite.height)
###----------------------------------------------------------------------
# <load_videos>()
# Inputs : None
# Output : None
# Description :
# Loads the images from the provided video folders for each state and
# stores them for future use.
###----------------------------------------------------------------------
def load_videos(self):
self.video_captures = {}
for state, path in self.videos.items():
image_folder = f'UiElements/{path}'
image_files = sorted([f for f in os.listdir(image_folder) if f.endswith('.png')])
images = [pyglet.image.load(os.path.join(image_folder, f)) for f in image_files]
self.video_captures[state] = images
if self.verbose:
print(f"Loaded images for state {state}")
###----------------------------------------------------------------------
# <set_state>()
# Inputs :
# - `new_state`: The state to transition to (e.g., 'idle', 'hover_idle').
# Output : None
# Description :
# Switches the button to the specified state, updating the current video
# to the corresponding image sequence. Determines whether the video should
# loop based on the state.
###----------------------------------------------------------------------
def set_state(self, new_state):
if new_state in self.video_captures:
if self.verbose:
print(f"Switching to {new_state}")
self.state = new_state
self.current_video = self.video_captures[new_state]
self.current_frame = 0
self.loop = new_state in ['idle', 'hover_idle', 'press_idle']
###----------------------------------------------------------------------
# <on_eos>()
# Inputs : None
# Output : None
# Description :
# Handles the end of a video stream (EOS) for transition states.
# Automatically sets the appropriate idle state after a transition.
###----------------------------------------------------------------------
def on_eos(self):
if self.verbose:
print(f"End of stream for state {self.state}")
if self.state in ['hover_transition', 'dehover_transition', 'press_transition', 'unpress_transition']:
if self.state == 'hover_transition':
self.set_state('hover_idle')
elif self.state == 'dehover_transition':
self.set_state('idle')
elif self.state == 'press_transition':
self.set_state('press_idle')
elif self.state == 'unpress_transition':
self.set_state('idle')
###----------------------------------------------------------------------
# <update_sprite>()
# Inputs : None
# Output : None
# Description :
# Updates the sprite to display the next frame in the current video sequence.
# Handles end-of-sequence logic and scales the sprite according to the button size.
###----------------------------------------------------------------------
def update_sprite(self):
if self.current_video:
# Increment the index and loop back to 0 if necessary
self.current_frame = (self.current_frame + 1) % len(self.current_video)
# Check if we need to handle end of sequence (EOS)
if not self.loop and self.current_frame == 0:
self.on_eos() # Call end-of-sequence handler
return
# Get the current image data
image_data = self.current_video[self.current_frame]
if self.sprite:
self.sprite.delete()
self.sprite = pyglet.sprite.Sprite(image_data, x=self.x, y=self.y, batch=self.batch, group=self.group)
self.sprite.scale = min(self.button_width / image_data.width, self.button_height / image_data.height)
if self.verbose:
print(f"Sprite updated for {self.state}. Index: {self.current_frame}")
###----------------------------------------------------------------------
# <on_mouse_motion>()
# Inputs :
# - `x`, `y`: Current mouse coordinates.
# - `dx`, `dy`: Change in mouse coordinates.
# Output : None
# Description :
# Checks if the mouse is over the button. If so, it transitions to
# the hover state; otherwise, it transitions back to the idle state.
###----------------------------------------------------------------------
def on_mouse_motion(self, x, y, dx, dy):
if self.is_mouse_over(x, y):
if self.state == 'idle':
self.set_state('hover_transition')
else:
if self.state == 'hover_idle':
self.set_state('dehover_transition')
###----------------------------------------------------------------------
# <on_mouse_press>()
# Inputs :
# - `x`, `y`: Mouse click coordinates.
# - `button`: Mouse button pressed.
# - `modifiers`: Modifier keys pressed during the click.
# Output : None
# Description :
# Checks if the mouse press is inside the button. If true, it transitions
# to the press state and triggers the toggle callback, if provided.
###----------------------------------------------------------------------
def on_mouse_press(self, x, y, button, modifiers):
if self.is_inside(x, y):
self.set_state('press_transition')
if self.on_toggle_callback:
self.on_toggle_callback(self.state)
###----------------------------------------------------------------------
# <is_inside>()
# Inputs :
# - `x`, `y`: Coordinates to check.
# Output :
# - Boolean: True if the coordinates are inside the button; otherwise, False.
# Description :
# Determines if a given set of coordinates is inside the button's boundaries.
###----------------------------------------------------------------------
def is_inside(self, x, y):
return (self.x <= x <= self.x + self.button_width and
self.y <= y <= self.y + self.button_height)
###----------------------------------------------------------------------
# <on_mouse_release>()
# Inputs :
# - `x`, `y`: Mouse release coordinates.
# - `button`: Mouse button released.
# - `modifiers`: Modifier keys pressed during the release.
# Output : None
# Description :
# Checks if the mouse release occurs over the button. If so, it
# transitions to the release state.
###----------------------------------------------------------------------
def on_mouse_release(self, x, y, button, modifiers):
if self.is_mouse_over(x, y):
self.set_state('press_deTransition')
###----------------------------------------------------------------------
# <is_mouse_over>()
# Inputs :
# - `x`, `y`: Mouse coordinates to check.
# Output :
# - Boolean: True if the mouse is over the button; otherwise, False.
# Description :
# Determines if the mouse is currently over the button.
###----------------------------------------------------------------------
def is_mouse_over(self, x, y):
return self.x <= x <= self.x + self.button_width and self.y <= y <= self.y + self.button_height
###----------------------------------------------------------------------
# <on_mouse_over>()
# Inputs : None
# Output : None
# Description :
# Transitions the button to the hover state when the mouse moves over it.
###----------------------------------------------------------------------
def on_mouse_over(self):
self.set_state('hover_transition')
###----------------------------------------------------------------------
# <on_mouse_out>()
# Inputs : None
# Output : None
# Description :
# Transitions the button back to the idle state when the mouse moves out.
###----------------------------------------------------------------------
def on_mouse_out(self):
self.set_state('dehover_transition')
###----------------------------------------------------------------------
# <draw>()
# Inputs : None
# Output : None
# Description :
# Draws the current sprite on the screen, updating its position and
# rendering it within the button.
###----------------------------------------------------------------------
def draw(self):
if self.sprite:
self.sprite.update(x=self.x, y=self.y)
self.sprite.draw()
###----------------------------------------------------------------------
# <update>()
# Inputs :
# - `dt`: The time delta since the last update.
# Output : None
# Description :
# Periodically updates the button, particularly the sprite, by
# advancing to the next frame in the video sequence.
###----------------------------------------------------------------------
def update(self, dt):
self.update_sprite()
class uxAnimation(pyglet.event.EventDispatcher):
def __init__(self, x, y, width, height, WINDOW_WIDTH, WINDOW_HEIGHT, animation_folder, batch, group=None, verbose=True):
self.x = x
self.y = y
self.original_width = width
self.original_height = height
self.window_width = width
self.window_height = height
self.batch = batch
self.group = group if group is not None else pyglet.graphics.Group(order=0)
self.animation_folder = animation_folder
self.verbose = verbose
self.frames = self.load_images(animation_folder)
self.current_frame = 0
self.sprite = None
self.animation_complete = False
self.initial_window_width = WINDOW_WIDTH
self.initial_window_height = WINDOW_HEIGHT
self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
self.update_sprite()
def load_images(self, path):
image_folder = f'UiElements/{path}'
image_files = sorted([f for f in os.listdir(image_folder) if f.endswith('.png')])
images = [pyglet.image.load(os.path.join(image_folder, f)) for f in image_files]
if self.verbose:
print(f"Loaded {len(images)} frames from {path}")
return images
def resize(self, window_width, window_height):
width_factor = window_width / self.initial_window_width
height_factor = window_height / self.initial_window_height
new_width = int(self.original_width * width_factor)
new_height = int(self.original_height * height_factor)
aspect_ratio = self.original_width / self.original_height
if new_width / new_height > aspect_ratio:
new_width = int(new_height * aspect_ratio)
else:
new_height = int(new_width / aspect_ratio)
self.window_width = new_width
self.window_height = new_height
if self.verbose:
print(f"Animation resized to {self.window_width}x{self.window_height}")
if self.sprite:
self.sprite.scale = min(self.window_width / self.sprite.width, self.window_height / self.sprite.height)
def play_animation(self):
self.current_frame = 0
self.animation_complete = False
self.update_sprite()
def update_sprite(self):
if self.current_frame < len(self.frames):
image_data = self.frames[self.current_frame]
if self.sprite:
self.sprite.delete()
self.sprite = pyglet.sprite.Sprite(image_data, x=self.x, y=self.y, batch=self.batch, group=self.group)
self.sprite.scale = min(self.window_width / image_data.width, self.window_height / image_data.height)
if self.verbose:
print(f"Displaying frame {self.current_frame + 1} of {len(self.frames)}")
self.current_frame += 1
else:
self.animation_complete = True
if self.verbose:
print("Animation complete.")
def update(self, dt):
if not self.animation_complete:
self.update_sprite()
def draw(self):
if self.sprite:
self.sprite.update(x=self.x, y=self.y)
self.sprite.draw()
###########################################################################################
# Example Execution
###########################################################################################
# # Define the window
# window = pyglet.window.Window(width=1920, height=1080)
# # Define the batch for drawing all sprites together
# main_batch = pyglet.graphics.Batch()
# # Example usage paths for mainWindowFrame and mainWindowFrameLoop
# entrance_folder = 'mainWindowFrame_frames' # Folder containing entrance animation PNGs
# loop_folder = 'mainWindowFrameLoop_frames' # Folder containing loop animation PNGs
# # Create an instance of MainWindowFrame
# main_window_frame = MainWindowFrame(
# x=0,
# y=0,
# width=1920,
# height=1080,
# WINDOW_WIDTH=1920,
# WINDOW_HEIGHT=1080,
# entrance_videos=entrance_folder,
# loop_videos=loop_folder,
# batch=main_batch,
# verbose=True
# )
# # Define the update function for the pyglet clock
# def update(dt):
# main_window_frame.update(dt)
# # Schedule the update function
# pyglet.clock.schedule_interval(update, 1/60.0) # Update at 60 FPS
# @window.event
# def on_draw():
# window.clear()
# main_batch.draw()
# @window.event
# def on_resize(width, height):
# main_window_frame.resize(width, height)
# # Run the application
# pyglet.app.run()
###########################################################################################
# Example Execution
###########################################################################################
# # Main application window
# window = pyglet.window.Window(800, 600, caption='Main Application Window', resizable=True)
# batch = pyglet.graphics.Batch()
# # Example usage of VideoButton
# detectButtonStateVisuals = {
# 'idle': 'detectIdleState_15M.mp4',
# 'hover_transition': 'detectHoverTransition_15M.mp4',
# 'hover_idle': 'detectHoverIdle_15M.mp4',
# 'dehover_transition': 'detectUnhoverTransition_15M.mp4',
# 'press_transition': 'detectPressedTransition_15M.mp4',
# 'press_idle': 'detectPressedIdle_15M.mp4',
# 'unpress_transition': 'detectUnpressedTransition_15M.mp4'
# }
# trainButtonStateVisuals = {
# 'idle': 'trainIdleState_15M.mp4',
# 'hover_transition': 'trainHoverTransition_15M.mp4',
# 'hover_idle': 'trainHoverIdle_15M.mp4',
# 'dehover_transition': 'trainUnhoverTransition_15M.mp4',
# 'press_transition': 'trainPressedTransition_15M.mp4',
# 'press_idle': 'trainPressedIdle_15M.mp4',
# 'unpress_transition': 'trainUnpressedTransition_15M.mp4'
# }
# video_button = VideoButton(1, 1, 30, 30, trainButtonStateVisuals, batch, False)
# @window.event
# def on_draw():
# window.clear()
# video_button.draw()
# @window.event
# def on_mouse_motion(x, y, dx, dy):
# video_button.on_mouse_motion(x, y, dx, dy)
# @window.event
# def on_mouse_press(x, y, button, modifiers):
# video_button.on_mouse_press(x, y, button, modifiers)
# @window.event
# def on_mouse_release(x, y, button, modifiers):
# video_button.on_mouse_release(x, y, button, modifiers)
# @window.event
# def on_resize(width, height):
# video_button.resize(width, height, maintain_initial_size=True)
# pyglet.clock.schedule_interval(video_button.update, 1/60) # 60 Hz update rate
# pyglet.app.run()