Created
September 20, 2014 03:49
-
-
Save clayote/216e2c94dc3788c8a86d to your computer and use it in GitHub Desktop.
Attempting to stack ImageStack widgets atop one another, instead they go behind.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from kivy.uix.widget import Widget | |
from kivy.core.image import Image | |
from kivy.graphics import Rectangle | |
from kivy.properties import ( | |
ListProperty, | |
DictProperty | |
) | |
from kivy.clock import Clock | |
from kivy.resources import resource_find | |
from kivy.app import App | |
class TextureStack(Widget): | |
"""Several textures superimposed on one another, and possibly offset | |
by some amount. | |
In 2D games where characters can wear different clothes or hold | |
different equipment, their graphics are often composed of several | |
graphics layered on one another. This widget simplifies the | |
management of such compositions. | |
""" | |
texs = ListProperty([]) | |
"""Texture objects""" | |
stackhs = ListProperty([]) | |
"""Stacking heights. These are how high (in the y dimension) above the | |
previous texture a texture at the same index in ``texs`` should be.""" | |
texture_rectangles = DictProperty({}) | |
"""Private. | |
Rectangle instructions for each of the textures, keyed by the | |
texture. | |
""" | |
def __init__(self, **kwargs): | |
"""Make triggers, bind, and if I have something to show, show it.""" | |
kwargs['size_hint'] = (None, None) | |
super().__init__(**kwargs) | |
self._trigger_upd_texs = Clock.create_trigger( | |
self._upd_texs, timeout=-1 | |
) | |
self._trigger_upd_pos = Clock.create_trigger( | |
self._upd_pos, timeout=-1 | |
) | |
self.bind(pos=self._trigger_upd_pos) | |
def on_texs(self, *args): | |
if len(self.texs) > 0: | |
self._trigger_upd_texs() | |
self._trigger_upd_pos() | |
def _upd_texs(self, *args): | |
if not self.canvas: | |
Clock.schedule_once(self.upd_texs, 0) | |
return | |
self._clear_rects() | |
w = h = 0 | |
stackh = 0 | |
i = 0 | |
for tex in self.texs: | |
self.canvas.add(self._rectify(tex, self.x, self.y + stackh)) | |
if tex.width > w: | |
w = tex.width | |
if tex.height > h: | |
h = tex.height | |
stackh += self.stackhs[i] | |
i += 1 | |
self.size = (w, h) | |
def _upd_pos(self, *args): | |
for rect in self.texture_rectangles.values(): | |
rect.pos = self.pos | |
def _clear_rects(self): | |
for rect in self.texture_rectangles.values(): | |
self.canvas.remove(rect) | |
self.texture_rectangles = {} | |
def clear(self): | |
self._clear_rects() | |
self.texs = [] | |
self.stackhs = [] | |
self.size = [1, 1] | |
def _rectify(self, tex, x, y): | |
rect = Rectangle( | |
pos=(x, y), | |
size=tex.size, | |
texture=tex | |
) | |
self.width = max([self.width, tex.width]) | |
self.height = max([self.height, tex.height]) | |
self.texture_rectangles[tex] = rect | |
return rect | |
def insert(self, i, tex): | |
if not self.canvas: | |
Clock.schedule_once( | |
lambda dt: TextureStack.insert( | |
self, i, tex), 0) | |
return | |
if len(self.stackhs) < len(self.texs): | |
self.stackhs.extend([0] * (len(self.texs) - len(self.stackhs))) | |
self.texs.insert(i, tex) | |
def append(self, tex): | |
self.insert(len(self.texs), tex) | |
def __delitem__(self, i): | |
tex = self.texs[i] | |
try: | |
rect = self.texture_rectangles[tex] | |
self.canvas.remove(rect) | |
del self.texture_rectangles[tex] | |
except KeyError: | |
pass | |
del self.stackhs[i] | |
del self.texs[i] | |
def __setitem__(self, i, v): | |
if len(self.texs) > 0: | |
self.unbind(texs=self._upd_texs) | |
self.__delitem__(i) | |
self.bind(texs=self._upd_texs) | |
self.insert(i, v) | |
def pop(self, i=-1): | |
self.stackhs.pop(i) | |
return self.texs.pop(i) | |
class ImageStack(TextureStack): | |
"""Instead of supplying textures themselves, supply paths to where the | |
texture may be loaded from.""" | |
paths = ListProperty() | |
def on_paths(self, *args): | |
super().clear() | |
for path in self.paths: | |
super().append(Image.load(resource_find(path)).texture) | |
def clear(self): | |
self.paths = [] | |
super().clear() | |
def insert(self, i, v): | |
if isinstance(v, str): | |
self.paths.insert(i, v) | |
else: | |
super().insert(i, v) | |
def append(self, v): | |
if isinstance(v, str): | |
self.paths.append(v) | |
else: | |
super().append(v) | |
def __delitem__(self, i): | |
super().__delitem__(i) | |
del self.paths[i] | |
def pop(self, i=-1): | |
self.paths.pop(i) | |
return super().pop(i) | |
class StackTestApp(App): | |
def build(self): | |
# replace these with some pngs on your hard drive | |
r = ImageStack(paths=['orb.png']) | |
r.add_widget(ImageStack(paths=['locked.png'])) | |
r.add_widget(ImageStack(paths=['unlocked.png'])) | |
return r | |
StackTestApp().run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment