Skip to content

Instantly share code, notes, and snippets.

@revolunet
Created July 27, 2012 09:10
Show Gist options
  • Select an option

  • Save revolunet/3186963 to your computer and use it in GitHub Desktop.

Select an option

Save revolunet/3186963 to your computer and use it in GitHub Desktop.
sample kivy image mixer
#:kivy 1.3
<WonPopup>:
title: 'YOU WIN'
auto_dismiss: False
size_hint: None, None
size: 300, 200
btn_restart: btn_restart
btn_close: btn_close
content: content
BoxLayout:
id: content
orientation: 'vertical'
Label:
text: 'Image complete, you won !'
size_hint: 1, None
height: 100
BoxLayout:
orientation: 'horizontal'
size_hint: 1, None
height: 50
Button:
id: btn_restart
text: 'restart'
Button:
id: btn_close
text: 'close'
# -*- encoding: UTF-8 -*-
#
# kivy ImgMixer game by [email protected]
#
# needs an img subfolder with any images
#
# Todo :
# - background helper
# - animation when merged stripes (blink/vibrate?)
# - improve bring to front behaviour (animation?)
# - when stripes hover another, use alpha ?
#
import os
import random
import kivy
kivy.require('1.3.0')
from kivy.app import App
from kivy.logger import Logger
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.core.image import Image as KivyImage
from kivy.graphics.vertex_instructions import Rectangle
from kivy.uix.scatter import Scatter
from kivy.uix.popup import Popup
def get_stripes_width(width, height, nb_stripes=10):
""" generate list of random stripes based on given width """
width_left = width
base_strip_width = width_left / nb_stripes
randomness = 0.3
min_strip_width = int(base_strip_width * (1 - randomness))
max_strip_width = int(base_strip_width * (1 + randomness))
parts = []
while width_left > 0:
stripe_width = random.randint(min_strip_width, max_strip_width)
if width_left - stripe_width < 0:
stripe_width = width_left
if stripe_width < min_strip_width:
# replace last stripe if too small
Logger.warn('Added a missing piece: %spx' % stripe_width)
last_part = parts[-1]
stripe_width += last_part[1]
width_left += last_part[1]
parts.pop()
if stripe_width > 0:
parts.append((width - width_left, stripe_width))
width_left -= stripe_width
return parts
class StripeWidget(Scatter):
TOLERANCE = 10 # how many max pixels for magnetic position
BLINK_DURATION = 0.5
def __init__(self, start_pos, *args, **kwargs):
self.start_pos = start_pos # hold original start position
self.do_rotation = False
self.do_scale = False
self.do_translation_y = False
super(StripeWidget, self).__init__(*args, **kwargs)
def get_siblings(self, sort=False):
if not self.parent:
return []
siblings = [stripe for stripe in self.parent.children if stripe is not self and isinstance(stripe, self.__class__)]
if sort:
siblings = sorted(siblings, key=lambda a: a.x)
return siblings
def on_touch_move(self, touch):
super(StripeWidget, self).on_touch_move(touch)
# to be sure we always collides, event if object have moved and mouse is not over any more
if touch.grab_state and touch.grab_list and touch.grab_list[0]() == self:
# test if near another stripe or out of the window bounds
if not self.parent:
return True
app_width = self.get_parent_window().width
if self.x < self.TOLERANCE and self.x != 0:
self.x = 0
return True
elif self.right > app_width - self.TOLERANCE and self.right != app_width:
self.right = app_width
return True
else:
for stripe in self.get_siblings():
# move widgets if they are close (magnetism)
collide_right = self.is_near_widget_right(stripe)
collide_left = self.is_near_widget_left(stripe)
if collide_left:
if self.x != stripe.right:
self.x = stripe.right
#return True
elif collide_right:
if self.right != stripe.x:
self.right = stripe.x
#return True
def on_touch_down(self, touch):
collides = super(StripeWidget, self).on_touch_down(touch)
if collides:
Logger.debug('ImgMix: on_touch_down %s' % self.start_pos)
kivy.app.App.get_running_app().layout.detect_stripes()
return True
def on_touch_up(self, touch):
collides = super(StripeWidget, self).on_touch_up(touch)
if collides:
if not touch.grab_state:
Logger.debug('ImgMix: on_touch_up %s' % self.start_pos)
kivy.app.App.get_running_app().layout.detect_stripes()
kivy.app.App.get_running_app().layout.show_hidden_stripes()
return True
def is_near_widget_right(self, wid):
return abs(self.right - wid.x) < self.TOLERANCE
def is_near_widget_left(self, wid):
return abs(self.x - wid.right) < self.TOLERANCE
def collide_widget(self, wid, tolerance=10):
if (self.right < wid.x - self.TOLERANCE):
return False
if (self.x > wid.right + self.TOLERANCE):
return False
if self.top < wid.y - self.TOLERANCE:
return False
if self.y > wid.top + self.TOLERANCE:
return False
return True
class WonPopup(Popup):
""" popup when user won """
def __init__(self, *args, **kwargs):
super(WonPopup, self).__init__(*args, **kwargs)
self.btn_restart.bind(on_release=self.restart)
self.btn_close.bind(on_release=self.close)
def restart(self, button):
self.dismiss()
kivy.app.App.get_running_app().restart()
def close(self, button):
self.dismiss()
kivy.app.App.get_running_app().close()
class DemoLayout(FloatLayout):
def __init__(self, *args, **kwargs):
super(DemoLayout, self).__init__(*args, **kwargs)
self.img = None
self.start()
def start(self):
self.build_image()
def show_hidden_stripes(self):
""" bring to front hidden widgets """
stripes = [item for item in self.children if isinstance(item, StripeWidget)]
stripes = sorted(stripes, key=lambda a: a.x)
for i, stripe1 in enumerate(stripes):
for j, stripe2 in enumerate(stripes):
if stripe2.x <= stripe1.right and stripe2.x >= stripe1.x:
stripe2._bring_to_front()
def merge_stripes(self, first, second):
""" merges two stripes widget in one and removes the originals """
self.remove_widget(first)
self.remove_widget(second)
length = first.width + second.width
pos = (first.x, 0)
size = (length, first.height)
w = StripeWidget(first.start_pos, pos=pos, width=length, height=first.height, size_hint=(None, None))
rect = Rectangle(texture=self.img.texture.get_region(first.start_pos, 0, length, first.height), size=size)
w.canvas.add(rect)
self.add_widget(w)
Logger.warn('ImgMix: creates a merged widget')
self.detect_stripes()
def detect_stripes(self):
""" try to detect well positionned stripes to merge them """
# todo : may be optimised ?
stripes = [item for item in self.children if isinstance(item, StripeWidget)]
stripes = sorted(stripes, key=lambda a: a.x)
merge = None
for i, stripe1 in enumerate(stripes):
for j, stripe2 in enumerate(stripes):
if i == j:
continue
is_after = (stripe2.x == stripe1.right) and (stripe2.start_pos == stripe1.start_pos + stripe1.width)
if is_after:
merge = (stripe1, stripe2)
break
is_before = (stripe2.right == stripe1.x) and (stripe2.start_pos + stripe2.width == stripe1.start_pos)
if is_before:
merge = (stripe2, stripe1)
break
if merge:
self.merge_stripes(*merge)
return
self.check_won()
def check_won(self):
""" detect if all striped merged """
stripes = [item for item in self.children if isinstance(item, StripeWidget)]
Logger.debug('ImgMix: stripes left : %s' % len(stripes))
if len(stripes) == 1:
stripes[0].x = 0
Logger.debug('ImgMix: user won')
popup = WonPopup(width=300, height=200)
popup.open()
def build_stripe(self, x, width, original_x):
""" creates a stripe widget with the correct texture """
height = self.img.height
pos = (x, 0)
size = (width, height)
stripe = StripeWidget(original_x, pos=pos, width=width, height=height, size_hint=(None, None))
rect = Rectangle(texture=self.img.texture.get_region(original_x, 0, width, height), size=size)
stripe.canvas.add(rect)
return stripe
def build_image(self, nb_stripes=10):
""" load an image, generates random stripes and add them to the layout """
self.clear_widgets()
images = ['img/%s' % img for img in os.listdir('img') if img.lower().endswith(('jpg', 'jpeg'))]
self.img = KivyImage(random.choice(images))
stripes = get_stripes_width(self.img.width, self.img.height, nb_stripes=nb_stripes)
random.shuffle(stripes)
widget = Widget()
self.add_widget(widget)
x = 0
for start, length in stripes:
stripe = self.build_stripe(x, length, start)
self.add_widget(stripe)
x += length
class ImgMixerApp(App):
def build(self):
self.layout = DemoLayout()
return self.layout
def restart(self):
self.layout.start()
def close(self):
kivy.app.stopTouchApp()
ImgMixerApp().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment