Created
July 27, 2012 09:10
-
-
Save revolunet/3186963 to your computer and use it in GitHub Desktop.
sample kivy image mixer
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
| #: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' |
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
| # -*- 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