mirror of https://github.com/kivy/kivy.git
229 lines
6.5 KiB
Python
229 lines
6.5 KiB
Python
from kivy.app import App
|
|
from kivy.uix.boxlayout import BoxLayout
|
|
from kivy.uix.scatter import Scatter
|
|
from kivy.uix.popup import Popup
|
|
from kivy.properties import ObjectProperty, StringProperty
|
|
from kivy.graphics import Line, Color, Point
|
|
|
|
from math import sqrt
|
|
from os import walk
|
|
from os.path import dirname, join
|
|
|
|
from kivy.lang import Builder
|
|
|
|
Builder.load_string('''
|
|
#:import os os
|
|
<Picture>:
|
|
# each time a picture is created, the image can delay the loading
|
|
# as soon as the image is loaded, ensure that the center is changed
|
|
# to the center of the screen.
|
|
on_size: self.center = app.main_root_widget.center
|
|
size: image.size
|
|
size_hint: None, None
|
|
on_touch_down: if self.collide_point(*args[1].pos): app.current_image = image
|
|
|
|
Image:
|
|
id: image
|
|
source: root.source
|
|
|
|
# create initial image to be 400 pixels width
|
|
size: 400, 400 / self.image_ratio
|
|
|
|
# add shadow background
|
|
canvas.before:
|
|
Color:
|
|
rgba: 1,1,1,1
|
|
BorderImage:
|
|
source: '../demo/pictures/shadow32.png'
|
|
border: (36,36,36,36)
|
|
size:(self.width+72, self.height+72)
|
|
pos: (-36,-36)
|
|
|
|
<ColorSelector>:
|
|
color: 1, 1, 1, 1
|
|
title: 'Color Slector'
|
|
content:content
|
|
BoxLayout:
|
|
id: content
|
|
orientation: 'vertical'
|
|
ColorPicker:
|
|
id: clr_picker
|
|
color: root.color
|
|
BoxLayout:
|
|
size_hint_y: None
|
|
height: '27sp'
|
|
Button:
|
|
text: 'ok'
|
|
on_release:
|
|
root.color = clr_picker.color
|
|
root.dismiss()
|
|
Button:
|
|
text: 'cancel'
|
|
on_release: root.dismiss()
|
|
|
|
<LeftPanel@BoxLayout>
|
|
orientation: 'vertical'
|
|
padding: '2pt'
|
|
canvas.before:
|
|
Color:
|
|
rgba: .5, .4, .9, .2
|
|
Rectangle:
|
|
pos: self.pos
|
|
size: self.size
|
|
Label:
|
|
size_hint_y: None
|
|
font_size: '18sp'
|
|
text_size: self.width, None
|
|
valign: 'middle'
|
|
halign: 'center'
|
|
height: self.texture.size[1] if self.texture else 10
|
|
text: 'Selected Image:\\n' + app.current_image.source.split(os.sep)[2] if app.current_image else 'None'
|
|
Button:
|
|
text: 'Brush'
|
|
size_hint_y: None
|
|
height: self.parent.width
|
|
on_release:
|
|
app.color_selector.open()
|
|
app.color_mode = 'brush'
|
|
Image:
|
|
color: app.color_selector.color
|
|
source: '../demo/touchtracer/particle.png'
|
|
allow_stretch: True
|
|
size: self.parent.size
|
|
pos: self.parent.pos
|
|
Button:
|
|
text: 'cursor'
|
|
on_release: app.color_mode = 'cursor'
|
|
Button:
|
|
text: 'clear'
|
|
on_release:
|
|
app.current_image.canvas.after.clear()
|
|
|
|
<MainRootWidget>
|
|
current_image: None
|
|
client_area: client_area
|
|
RelativeLayout:
|
|
id: client_area
|
|
Splitter:
|
|
sizable_from: 'left'
|
|
size_hint: None, 1
|
|
width: '99dp'
|
|
LeftPanel:
|
|
|
|
''')
|
|
|
|
|
|
def calculate_points(x1, y1, x2, y2, steps=5):
|
|
dx = x2 - x1
|
|
dy = y2 - y1
|
|
dist = sqrt(dx * dx + dy * dy)
|
|
if dist < steps:
|
|
return None
|
|
o = []
|
|
m = dist / steps
|
|
for i in range(1, int(m)):
|
|
mi = i / m
|
|
lastx = x1 + dx * mi
|
|
lasty = y1 + dy * mi
|
|
o.extend([lastx, lasty])
|
|
return o
|
|
|
|
|
|
class ColorSelector(Popup):
|
|
pass
|
|
|
|
|
|
class Picture(Scatter):
|
|
|
|
source = StringProperty(None)
|
|
'''path to the Image to be loaded
|
|
'''
|
|
|
|
def __init__(self, **kwargs):
|
|
super(Picture, self).__init__(**kwargs)
|
|
self._app = App.get_running_app()
|
|
|
|
def on_touch_down(self, touch):
|
|
_app = self._app
|
|
if (_app.color_mode[0] == 'c' or
|
|
not self.collide_point(*touch.pos)):
|
|
return super(Picture, self).on_touch_down(touch)
|
|
ud = touch.ud
|
|
ud['group'] = g = str(touch.uid)
|
|
_pos = list(self.ids.image.to_widget(*touch.pos))
|
|
_pos[0] += self.parent.x
|
|
with self.ids.image.canvas.after:
|
|
ud['color'] = Color(*_app.color_selector.color, group=g)
|
|
ud['lines'] = Point(points=(_pos),
|
|
source='../examples/demo/touchtracer/particle.png',
|
|
pointsize=5, group=g)
|
|
touch.grab(self)
|
|
return True
|
|
|
|
def on_touch_move(self, touch):
|
|
if touch.grab_current is not self:
|
|
return
|
|
_app = self._app
|
|
if _app.color_mode[0] == 'c' or not self.collide_point(*touch.pos):
|
|
return super(Picture, self).on_touch_move(touch)
|
|
ud = touch.ud
|
|
_pos = list(self.ids.image.to_widget(*touch.pos))
|
|
_pos[0] += self.parent.x
|
|
points = ud['lines'].points
|
|
oldx, oldy = points[-2], points[-1]
|
|
points = calculate_points(oldx, oldy, _pos[0], _pos[1])
|
|
if points:
|
|
try:
|
|
lp = ud['lines'].add_point
|
|
for idx in range(0, len(points), 2):
|
|
lp(points[idx], points[idx + 1])
|
|
except GraphicException:
|
|
pass
|
|
|
|
def on_touch_up(self, touch):
|
|
if touch.grab_current is not self:
|
|
return
|
|
_app = self._app
|
|
if _app.color_mode[0] == 'c':
|
|
return super(Picture, self).on_touch_up(touch)
|
|
touch.ungrab(self)
|
|
ud = touch.ud
|
|
self.canvas.remove_group(ud['group'])
|
|
|
|
|
|
class MainRootWidget(BoxLayout):
|
|
|
|
clent_area = ObjectProperty(None)
|
|
# The Client Area in which all editing is Done
|
|
|
|
def on_parent(self, instance, parent):
|
|
if parent:
|
|
_dir = join(dirname(__file__), 'lists/fruit_images/')
|
|
for image in list(walk(_dir))[0][2]:
|
|
if image.find('512') > -1:
|
|
self.client_area.add_widget(Picture(source=_dir + image))
|
|
|
|
|
|
class MainApp(App):
|
|
|
|
main_root_widget = ObjectProperty(None)
|
|
# we will be accessing this later as App.main_root_widget
|
|
|
|
current_image = ObjectProperty(None)
|
|
'''This is a handle to the currently selected image on which the effects
|
|
would be applied.'''
|
|
|
|
color_mode = StringProperty('cursor')
|
|
'''This defines the current mode `brush` or `cursor`. `brush` mode allows
|
|
adding brush strokes to the currently selected Image.
|
|
'''
|
|
|
|
def build(self):
|
|
self.color_selector = ColorSelector()
|
|
self.main_root_widget = MainRootWidget()
|
|
return self.main_root_widget
|
|
|
|
|
|
if __name__ == '__main__':
|
|
MainApp().run()
|