You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

253 lines
9.7 KiB

# -*- coding: utf-8 -*-
from kivy.uix.screenmanager import Screen
from kivy.uix.scatter import Matrix
from kivy.uix.scatterlayout import ScatterLayout
from kivy.uix.image import Image, AsyncImage
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.stencilview import StencilView
from kivy.uix.popup import Popup
from kivy.uix.button import ButtonBehavior
from kivy.factory import Factory
from kivy.properties import ObjectProperty, BooleanProperty, DictProperty, ListProperty, NumericProperty
from kivy.vector import Vector
from kivy.logger import Logger
from kivy.metrics import dp
from kivy.uix.colorpicker import ColorPicker
class ViewerScreen(Screen):
app = ObjectProperty(None)
dialog = ObjectProperty(None)
def on_pre_enter(self, *args):
if not self.dialog:
self.dialog = Factory.SlidesDialog()
try:
if not len(self.dialog.grid.children) == len(self.app.presentation['slides']):
Logger.debug('Application: Clean \& reload carousel.')
self.dialog.grid.clear_widgets()
self.carousel.clear_widgets()
for slide in reversed(self.app.presentation['slides']):
self.dialog.grid.add_widget(Factory.SlideButton(source=slide['thumb_src'],
size_hint=(None, None),
keep_ratio=True, ))
image = SlideBox(slide=slide)
self.carousel.add_widget(image)
except KeyError as msg:
Logger.debug('Application: Presentations seems empty. {msg}'.format(msg=msg))
def update_carousel(self):
self.dialog.grid.clear_widgets()
self.carousel.clear_widgets()
for slide in reversed(self.app.presentation['slides']):
self.dialog.grid.add_widget(Factory.SlideButton(source=slide['thumb_src'],
size_hint=(None, None),
keep_ratio=True, ))
image = SlideBox(slide=slide)
self.carousel.add_widget(image)
class TouchActionArea(FloatLayout):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos) and touch.is_double_tap and len(self.app.presentation['slides']) > 0:
try:
child = [child for child in self.children if child.collide_point(*touch.pos)][0]
except IndexError:
Logger.debug('Viewer: No TouchActionArea child touched.')
return
if child.name == 'center':
if len(self.app.root.current_screen.box.children) < 2:
Logger.info('Application: Switching to compare mode.')
self.parent.dialog.to_switch = False
self.parent.dialog.title = _('Compare to...')
self.parent.dialog.open()
else:
self.app.compare_slide(action='rm')
touch.ungrab(self)
return True
elif child.name == 'ltop':
self.app.root.current = 'editor'
return True
elif child.name == 'rtop':
pass
elif child.name == 'lbottom':
pass
elif child.name == 'rbottom':
self.parent.dialog.to_switch = True
self.parent.dialog.title = _('Switch to...')
self.parent.dialog.open()
return False
return super(TouchActionArea, self).on_touch_down(touch)
class SlideBox(BoxLayout, StencilView):
slide = DictProperty(None)
def on_touch_down(self, touch):
if not self.collide_point(*touch.pos):
return False
return super(SlideBox, self).on_touch_down(touch)
def on_touch_up(self, touch):
if not touch.grab_current == self:
return False
return super(SlideBox, self).on_touch_down(touch)
def on_size(self, *args):
try:
img_zoom = [child for child in self.float_layout.children if child.id == 'img_zoom'][0]
img_zoom.size = (self.size[0] / 6, (self.size[0] / 6) / img_zoom.image_ratio)
img_zoom.pos = [self.pos[0], 0.05 * dp(self.size[1])]
except IndexError:
pass
def get_caption(self):
artist = self.slide['artist'].decode('utf-8')
title = ''.join(('[i]', self.slide['title'].decode('utf-8'), '[/i]'))
year = self.slide['year']
caption = ' - '.join((artist, title, year))
return caption
class SlideViewer(ScatterLayout):
image = ObjectProperty(None)
app = ObjectProperty(None)
locked = BooleanProperty(False)
painter = ObjectProperty(None)
def on_touch_down(self, touch):
# Scaling scatter with mousewheel based on touch position.
if self.collide_point(*touch.pos) and touch.is_mouse_scrolling:
scale = self.scale
if touch.button == 'scrolldown':
scale = self.scale + 0.08
elif touch.button == 'scrollup':
scale = max(0.1, self.scale - 0.08)
rescale = scale * 1.0 / self.scale
matrix = Matrix().scale(rescale, rescale, rescale)
self.apply_transform(matrix, anchor=touch.pos)
self.check_slide_bbox()
return False
return super(SlideViewer, self).on_touch_down(touch)
def on_transform_with_touch(self, touch):
self.check_slide_bbox()
def check_slide_bbox(self):
if self.app.config.getint('viewer', 'thumb') == 1:
img_point = (self.bbox[0][0] + self.bbox[1][0],
self.bbox[0][1] + self.bbox[1][1])
parent_point = (self.parent.size[0] + self.parent.pos[0],
self.parent.size[1] + self.parent.pos[1])
if not Vector.in_bbox(self.bbox[0],
self.parent.pos,
parent_point) or not Vector.in_bbox(img_point,
self.parent.pos,
parent_point):
if all(child.id != 'img_zoom' for child in self.parent.children):
# TODO: Change thumbnail position and size based on config.
thumb = AsyncImage(source=self.image.source,
id='img_zoom',
size_hint=(None, None),
keep_ratio=True,
size=(self.parent.size[0] / 5,
(self.parent.size[0] / 5) / self.image.image_ratio),
pos=[0, 0.05 * self.parent.size[1]])
self.parent.add_widget(thumb)
elif min(self.bbox[0]) > 0:
img_zoom = [child for child in self.parent.children if child.id == 'img_zoom']
try:
self.parent.remove_widget(img_zoom[0])
except IndexError:
pass
def lock(self):
if not self.locked:
Logger.info('Application: Locking Slide.')
self.locked = True
self.do_rotation = False
self.do_scale = False
self.do_translation = False
toolbar = PainterToolBar(painter=self.painter,
paint_color=self.painter.tools[self.painter.current_tool]['color'],
thickness=self.painter.thickness)
self.painter.bind(thickness=toolbar.on_thickness)
self.painter.bind(color=toolbar.on_paint_color)
self.slidebox.toolbar.add_widget(toolbar)
self.app.root.get_screen('viewer').carousel.scroll_timeout = 50
elif self.locked:
Logger.info('Application: Unlocking Slide.')
self.locked = False
self.do_scale = True
self.do_translation = True
self.do_rotation = True
self.slidebox.toolbar.remove_widget(self.slidebox.toolbar.children[0])
self.app.root.get_screen('viewer').carousel.scroll_timeout = 200
def on_locked(self, *args):
self.slidebox.toolbar.lock_btn.text = {u'\uf13e': u'\uf023', u'\uf023': u'\uf13e'}[
self.slidebox.toolbar.lock_btn.text]
if self.painter:
self.painter.locked = {True: False, False: True}[self.locked]
class PainterToolBar(BoxLayout):
painter = ObjectProperty(None)
paint_color = ListProperty((1, 1, 1, 1))
thickness = NumericProperty(0.5)
def show_color_picker(self, current_color):
popup = Popup(title='Color Picker',
size_hint=(0.5, 0.5))
color_picker = ColorPicker(color=current_color)
color_picker.bind(color=self.on_paint_color)
popup.content = color_picker
popup.open()
def on_paint_color(self, instance, value):
self.paint_color = value
self.painter.color = value
def on_thickness(self, instance, value):
self.thickness = value
self.painter.thickness = value
class SlidesDialog(Popup):
to_switch = BooleanProperty(False)
class SlideButton(ButtonBehavior, Image):
app = ObjectProperty(None)
def on_release(self):
if self.parent.dialog.to_switch:
current_index = self.parent.children.index(self)
carousel_index = list(reversed(xrange(len(self.parent.children))))
self.app.switch_slide(carousel_index[current_index])
else:
self.app.compare_slide(self.parent.children.index(self))