]> vault307.fbx.one Git - PicoTamachibi.git/blob - icon.py
Add comment to main.py (#1)
[PicoTamachibi.git] / icon.py
1 import framebuf
2 from machine import Pin
3 from time import sleep
4 from os import listdir
5 import gc
6
7 class Icon():
8 """ Models an icon and all the properties it requires """
9 __image = None
10 __x = 0
11 __y = 0
12 __invert = False
13 __width = 16
14 __height = 16
15 __name = "Empty"
16
17
18 def __init__(self, filename:None, width=None, height=None, x=None, y=None, name=None):
19 """ Sets up the default values """
20 if width:
21 self.__width = width
22 if height:
23 self.__height = height
24 if name:
25 self.__name = name
26 if x:
27 self.__x = x
28 if y:
29 self.__y = y
30 if filename is not None:
31 self.__image = self.loadicons(filename)
32
33 @property
34 def image(self):
35 """ gets the icon image """
36
37 return self.__image
38
39 @image.setter
40 def image(self, buf):
41 """ Sets the icon image """
42
43 self.__image = buf
44
45 @property
46 def x(self)->int:
47 """ Gets the X value """
48
49 return self.__x
50
51
52 @x.setter
53 def x(self, value):
54 """ Sets the X value """
55
56 self.__x = value
57
58
59 @property
60 def width(self)->int:
61 """ Get the width"""
62
63 return self.__width
64
65
66 @width.setter
67 def width(self, value):
68 """ Sets the icon width """
69
70 self.__width = value
71
72
73 @property
74 def height(self):
75 """ Returns height """
76 return self.__height
77
78
79 @height.setter
80 def height(self, value):
81 """ Gets the height """
82
83 self.__height = value
84
85
86 @property
87 def name(self):
88 """ Gets the name """
89
90 return self.__name
91
92
93 @name.setter
94 def name(self, value):
95 """ Sets the icon name """
96
97 self.__name = value
98
99
100 @property
101 def y(self)->int:
102 """ Gets the y value """
103
104 return self.__y
105
106
107 @y.setter
108 def y(self, value):
109 """ Sets the Y value """
110
111 self.__y = value
112
113
114 @property
115 def invert(self)->bool:
116 """ Flips the bits in the image so white become black etc and returns the image """
117 print("Invert is", self.__invert)
118 return self.__invert
119
120 @invert.setter
121 def invert(self, value:bool):
122 """ Inverts the icon colour """
123
124 image = self.__image
125 for x in range(0,self.width):
126 for y in range(0, self.height):
127 pxl = image.pixel(x,y)
128 if pxl == 0:
129 image.pixel(x,y,1)
130 else:
131 image.pixel(x,y,0)
132
133 self.__image = image
134 self.__invert = value
135 # print("Invert is", self.__invert)
136
137 def loadicons(self, file):
138 # print(file)
139 with open(file, 'rb') as f:
140 f.readline() # magic number
141 f.readline() # creator comment
142 f.readline() # dimensions
143 data = bytearray(f.read())
144 fbuf = framebuf.FrameBuffer(data, self.__width,self.__height, framebuf.MONO_HLSB)
145 # print(self.__name, self.__width, self.__height)
146 return fbuf
147
148 class Toolbar():
149 """ Models the toolbar """
150 __icon_array = []
151 __framebuf = framebuf.FrameBuffer(bytearray(160*64*1), 160, 16, framebuf.MONO_HLSB)
152 __spacer = 1
153 __selected_item = None
154 __selected_index = -1 # -1 means no item selected
155
156 def __init__(self):
157 # print("building toolbar")
158 self.__framebuf = framebuf.FrameBuffer(bytearray(160*64*8), 160, 16, framebuf.MONO_HLSB)
159
160 def additem(self, icon):
161 self.__icon_array.append(icon)
162 print("mehr icons...")
163
164
165 def remove(self, icon):
166 self.__icon_array.remove(icon)
167
168 def getlength(self):
169 return len(self.__icon_array)
170
171 """
172 @property
173 def data(self):
174
175 x = 0
176 count = 0
177 for icon in self.__icon_array:
178 # print("x:",x)
179 count += 1
180 if type(icon) == Icon:
181 self.__framebuf.blit(icon.image, x, 0)
182 if type(icon) == Animate:
183 self.__framebuf.blit(icon.__frames[icon.__current_frame].image, x, 0)
184 fb = self.__framebuf
185 x += icon.width + self.spacer
186 return fb
187 """
188
189 @property
190 def spacer(self):
191 """ returns the spacer value"""
192 return self.__spacer
193
194 @spacer.setter
195 def spacer(self, value):
196 """ Sets the spacer value"""
197 self.__spacer = value
198
199 def show(self, oled):
200 oled.blit(self.__framebuf, 0,0)
201 # oled.show()
202
203 def select(self, index, oled):
204 """ Set the item in the index to inverted """
205 # for item in self.__icon_array:
206 # item.invert = False
207 self.__icon_array[index].invert = True
208 self.__selected_index = index
209 offsetvalue = 3
210 x=0
211 for loopcount in range(len(self.__icon_array)):
212 offset = loopcount - offsetvalue
213 zeiger = (index + offset) % len(self.__icon_array)
214 # print("x:",x)
215 if type(self.__icon_array[zeiger]) == Icon:
216 self.__framebuf.blit(self.__icon_array[zeiger].image, x, 0)
217 if type(self.__icon_array[zeiger]) == Animate:
218 self.__framebuf.blit(self.__icon_array[zeiger].__frames[self.__icon_array[zeiger].__current_frame].image, x, 0)
219 fb = self.__framebuf
220 x += self.__icon_array[zeiger].width + self.spacer
221 return fb
222
223 def unselect(self, index, oled):
224 self.__icon_array[index].invert = False
225 self.__selected_index = -1
226 self.show(oled)
227
228 @property
229 def selected_item(self):
230 """ Returns the name of the currently selected icon """
231 self.__selected_item = self.__icon_array[self.__selected_index].name
232 return self.__selected_item
233
234 class Animate():
235 __frames = []
236 __current_frame = 0
237 __speed = "normal" # Other speeds are 'fast' and 'slow' - it just adds frames or skips frames
238 __speed_value = 0
239 __done = False # Has the animation completed
240 __loop_count = 0
241 __bouncing = False
242 __animation_type = "default"
243 __pause = 0
244 __set = False
245 __x = 0
246 __y = 0
247 __width = 16
248 __height = 16
249 __cached = False
250 __filename = None
251 """ other animations types:
252 - loop
253 - bounce
254 - reverse
255 """
256 @property
257 def set(self)->bool:
258 return self.__set
259
260 @set.setter
261 def set(self, value:bool):
262 self.__set = value
263 if value == True:
264 self.load()
265 else:
266 self.unload()
267
268
269 @property
270 def speed(self):
271 """ Returns the current speed """
272 return self.__speed
273
274 @speed.setter
275 def speed(self, value:str):
276 if value in ['very slow','slow','normal','fast']:
277 self.__speed = value
278 if value == 'very slow':
279 self.__pause = 10
280 self.__speed_value = 10
281 if value == 'slow':
282 self.__pause = 1
283 self.__speed_value = 1
284 if value == "normal":
285 self.__pause = 0
286 self.__speed_value = 0
287 else:
288 print(value, "is not a valid value, try 'fast','normal' or 'slow'")
289
290 @property
291 def animation_type(self):
292 return self.__animation_type
293
294 @animation_type.setter
295 def animation_type(self, value):
296 if value in ['default','loop','bounce','reverse']:
297 self.__animation_type = value
298 else:
299 print(value," is not a valid Animation type - it should be 'loop','bounce','reverse' or 'default'")
300
301 def __init__(self, frames=None, animation_type:str=None,x:int=None,y:int=None, width:int=None, height:int=None, filename=None):
302 """ setup the animation """
303
304 if x:
305 self.__x = x
306 if y:
307 self.__y = y
308 if width:
309 self.__width = width
310 if height:
311 self.__height = height
312 self.__current_frame = 0
313 if frames is not None:
314 self.__frames = frames
315 self.__done = False
316 self.__loop_count = 1
317 if animation_type is not None:
318 self.animation_type = animation_type
319 if filename:
320 self.__filename = filename
321
322 @property
323 def filename(self):
324 """ Returns the current filename"""
325 return self.__filename
326
327 @filename.setter
328 def filename(self, value):
329 """ Sets the filename """
330 self.__filename = value
331
332 def forward(self):
333 """ progress the current frame """
334 if self.__speed == 'normal':
335 self.__current_frame +=1
336
337 if self.__speed in ['very slow','slow']:
338 if self.__pause > 0:
339 self.__pause -= 1
340 else:
341 self.__current_frame +=1
342 self.__pause = self.__speed_value
343
344 if self.__speed == 'fast':
345 if self.__current_frame < self.frame_count +2:
346 self.__current_frame +=2
347 else:
348 self.__current_frame +=1
349
350 def reverse(self):
351 if self.__speed == 'normal':
352 self.__current_frame -=1
353
354 if self.__speed in ['very slow','slow']:
355 if self.__pause > 0:
356 self.__pause -= 1
357 else:
358 self.__current_frame -=1
359 self.__pause = self.__speed_value
360
361 if self.__speed == 'fast':
362 if self.__current_frame < self.frame_count +2:
363 self.__current_frame -=2
364 else:
365 self.__current_frame -=1
366
367 def load(self):
368 """ load the animation files """
369
370 # load the files from disk
371 if not self.__cached:
372 files = listdir()
373 array = []
374 for file in files:
375 if (file.startswith(self.__filename)) and (file.endswith('.pbm')):
376 array.append(Icon(filename=file, width=self.__width, height=self.__height, x=self.__x, y=self.__y, name=file))
377 self.__frames = array
378 self.__cached = True
379
380 def unload(self):
381 """ free up memory """
382
383 self.__frames = None
384 self.__cached = False
385 gc.collect()
386
387 def animate(self, oled):
388 """ Animates the frames based on the animation type and for the number of times specified """
389
390 cf = self.__current_frame # Current Frame number - used to index the frames array
391 frame = self.__frames[cf]
392 oled.blit(frame.image, frame.x, frame.y)
393
394 if self.__animation_type == "loop":
395 # Loop from the first frame to the last, for the number of cycles specificed, and then set done to True
396 self.forward()
397
398 if self.__current_frame > self.frame_count:
399 self.__current_frame = 0
400 self.__loop_count -=1
401 if self.__loop_count == 0:
402 self.__done = True
403
404 if self.__animation_type == "bouncing":
405
406 # Loop from the first frame to the last, and then back to the first again, then set done to True
407 if self.__bouncing:
408
409 if self.__current_frame == 0:
410 if self.__loop_count == 0:
411 self.__done == True
412 else:
413 if self.__loop_count >0:
414 self.__loop_count -=1
415 self.forward()
416 self.__bouncing = False
417 if self.__loop_count == -1:
418 # bounce infinately
419 self.forward()
420 self.__bouncing = False
421 if (self.__current_frame < self.frame_count) and (self.__current_frame>0):
422 self.reverse()
423 else:
424 if self.__current_frame == 0:
425 if self.__loop_count == 0:
426 self.__done == True
427 elif self.__loop_count == -1:
428 # bounce infinatey
429 self.forward()
430 else:
431 self.forward()
432 self.__loop_count -= 1
433 elif self.__current_frame == self.frame_count:
434 self.reverse()
435 self.__bouncing = True
436 else:
437 self.forward()
438
439 if self.__animation_type == "default":
440 # loop through from first frame to last, then set done to True
441
442 if self.__current_frame == self.frame_count:
443 self.__current_frame = 0
444 self.__done = True
445 else:
446 self.forward()
447
448 @property
449 def frame_count(self):
450 """ Returns the total number of frames in the animation """
451 return len(self.__frames)-1
452
453 @property
454 def done(self):
455 """ Has the animation completed """
456 if self.__done:
457 self.__done = False
458 return True
459 else:
460 return False
461
462 def loop(self, no:int=None):
463 """ Loops the animation
464 if no is None or -1 the animation will continue looping until animate.stop() is called """
465
466 if no is not None:
467 self.__loop_count = no
468 else:
469 self.__loop_count = -1
470 self.__animation_type = "loop"
471
472 def stop(self):
473 self.__loop_count = 0
474 self.__bouncing = False
475 self.__done = True
476
477 def bounce(self, no:int=None):
478 """ Loops the animation forwared, then backward, the number of time specified in no,
479 if there is no number provided it will animate infinately """
480
481 self.__animation_type = "bouncing"
482 if no is not None:
483 self.__loop_count = no
484 else:
485 self.__loop_count = -1
486
487 @property
488 def width(self):
489 """ Gets the icon width """
490 return self.__width
491
492 @width.setter
493 def width(self, value):
494 """ Sets the icon width """
495 self.__width = value
496
497 @property
498 def height(self):
499 """ Gets the icon height """
500 return self.__width
501
502 @height.setter
503 def height(self, value):
504 """ Sets the icon height """
505 self.__height = value
506
507
508 class Button():
509 """ Models a button, check the status with is_pressed """
510
511 # The private variables
512 __pressed = False
513 __pin = 0
514 __button_down = False
515
516 def __init__(self, pin:int):
517 """ Sets up the button """
518
519 self.__pin = Pin(pin, Pin.IN, Pin.PULL_DOWN)
520 self.__pressed = False
521
522 @property
523 def is_pressed(self)->bool:
524 """ Returns the current state of the button """
525
526 if self.__pin.value() == 0:
527 self.__button_down = False
528 return False
529 if self.__pin.value() == 1:
530 if not self.__button_down:
531 # print("button pressed")
532 self.__button_down = True
533 return True
534 else:
535 return False
536
537 class Event():
538 """ Models events that can happen, with timers and pop up messages """
539 __name = ""
540 __value = 0
541 __sprite = None
542 __timer = -1 # -1 means no timer set
543 __timer_ms = 0
544 __callback = None
545 __message = ""
546
547 def __init__(self, name=None, sprite=None, value=None, callback=None):
548 """ Create a new event """
549
550 if name:
551 self.__name = name
552 if sprite:
553 self.__sprite = sprite
554 if value:
555 self.__value = value
556 if callback is not None:
557 self.__callback = callback
558
559
560 @property
561 def name(self):
562 """ Return the name of the event"""
563
564 return self.__name
565
566
567 @name.setter
568 def name(self, value):
569 """ Set the name of the Event"""
570
571 self.__name = value
572
573
574 @property
575 def value(self):
576 """ Gets the current value """
577 return self.__value
578
579
580 @value.setter
581 def value(self, value):
582 """ Sets the current value """
583 self.__value = value
584
585
586 @property
587 def sprite(self):
588 """ Gets the image sprite """
589
590 return self.__sprite
591
592
593 @sprite.setter
594 def sprite(self, value):
595 """ Sets the image sprite """
596
597 self.__value = value
598
599
600 @property
601 def message(self):
602 """ Return the message """
603
604 return self.__message
605
606
607 @message.setter
608 def message(self, value):
609 """ Set the message """
610 self.__message = value
611
612 def popup(self, oled):
613 # display popup window
614 # show sprite
615 # show message
616
617 fbuf = framebuf.FrameBuffer(bytearray(128 * 48 * 1), 128, 48, framebuf.MONO_HLSB)
618 fbuf.rect(0,0,128,48, 1)
619 fbuf.blit(self.sprite.image, 5, 10)
620 fbuf.text(self.message, 32, 18)
621 oled.blit(fbuf, 0, 16)
622 oled.show()
623 sleep(2)
624
625 @property
626 def timer(self):
627 """ Gets the current timer value """
628
629 return self.__timer
630
631
632 @timer.setter
633 def timer(self, value):
634 """ Sets the current timer value """
635
636 self.__timer = value
637
638
639 @property
640 def timer_ms(self):
641 """ Get the timer in MS """
642
643 return self.__timer_ms
644
645
646 @timer_ms.setter
647 def timer_ms(self, value):
648 """ Set the timer in MS """
649 self.__timer_ms = value
650
651
652 def tick(self):
653 """ Progresses the animation on frame """
654
655 self.__timer_ms += 1
656 if self.__timer_ms >= self.__timer:
657 if self.__callback is not None:
658 print("poop check callback")
659 self.__callback()
660 self.__timer = -1
661 self.__timer_ms = 0
662 else:
663 # print("Timer Alert!")
664 pass
665