Skip to content

Instantly share code, notes, and snippets.

@clayote
Created September 20, 2014 03:49
Show Gist options
  • Save clayote/216e2c94dc3788c8a86d to your computer and use it in GitHub Desktop.
Save clayote/216e2c94dc3788c8a86d to your computer and use it in GitHub Desktop.
Attempting to stack ImageStack widgets atop one another, instead they go behind.
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