Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c9f5736
Showing
1 changed file
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
#!/usr/bin/env python | ||
import kivy | ||
kivy.require('1.0.7') | ||
|
||
from os.path import dirname, isdir, join | ||
from kivy.animation import Animation | ||
from kivy.app import App | ||
from kivy.clock import Clock | ||
from kivy.core.window import Window | ||
from kivy.factory import Factory | ||
from kivy.graphics.fbo import Fbo | ||
from kivy.graphics import RenderContext | ||
from kivy.lang import Builder | ||
from kivy.properties import StringProperty, ObjectProperty, NumericProperty, \ | ||
BooleanProperty | ||
from kivy.resources import resource_add_path | ||
from kivy.uix.floatlayout import FloatLayout | ||
|
||
Builder.load_string(''' | ||
<SlideShaderContainer>: | ||
canvas: | ||
Rectangle: | ||
pos: self.pos | ||
size: self.size | ||
texture: self.fbo_texture | ||
<Slides>: | ||
container1: container1 | ||
container2: container2 | ||
SlidesBackground: | ||
slides: root | ||
SlideShaderContainer: | ||
id: container1 | ||
size_hint_y: None | ||
height: root.height - 35 | ||
y: 35 | ||
SlideShaderContainer: | ||
id: container2 | ||
size_hint_y: None | ||
height: root.height - 35 | ||
y: 35 | ||
SlidesForeground: | ||
slides: root | ||
<Slide>: | ||
pos_hint: {'x': 0, 'y': 0} | ||
''') | ||
|
||
fs_default = None | ||
vs_default = ''' | ||
$HEADER$ | ||
uniform float alpha; | ||
uniform vec2 size; | ||
void main (void) { | ||
frag_color = color; | ||
tex_coord0 = vTexCoords0; | ||
vec2 p = vPosition.xy; | ||
p.x += alpha * size.x; | ||
//p.y += alpha * - size.y; | ||
gl_Position = projection_mat * modelview_mat * vec4(p, 0.0, 1.0); | ||
} | ||
''' | ||
|
||
class SlidesBackground(FloatLayout): | ||
slides = ObjectProperty(None) | ||
|
||
class SlidesForeground(FloatLayout): | ||
slides = ObjectProperty(None) | ||
|
||
class Slide(FloatLayout): | ||
is_section = BooleanProperty(False) | ||
title = StringProperty(None) | ||
active = BooleanProperty(False) | ||
|
||
class SlideShaderContainer(FloatLayout): | ||
alpha = NumericProperty(0.) | ||
fbo = ObjectProperty(None) | ||
fbo_texture = ObjectProperty(None, allownone=True) | ||
vs = StringProperty(vs_default) | ||
fs = StringProperty(fs_default) | ||
|
||
def __init__(self, **kwargs): | ||
self.fbo = Fbo(size=self.size) | ||
self.fbo_texture = self.fbo | ||
self.canvas = RenderContext() | ||
self.canvas.shader.vs = self.vs | ||
self.canvas.shader.fs = self.fs | ||
self.canvas.add(self.fbo) | ||
Clock.schedule_interval(self.update_shader, 0) | ||
super(SlideShaderContainer, self).__init__(**kwargs) | ||
|
||
def _add_widget(self, widget): | ||
canvas = self.canvas | ||
self.canvas = self.fbo | ||
super(SlideShaderContainer, self).add_widget(widget) | ||
self.canvas = canvas | ||
|
||
def _remove_widget(self, widget): | ||
canvas = self.canvas | ||
self.canvas = self.fbo | ||
super(SlideShaderContainer, self).remove_widget(widget) | ||
self.canvas = canvas | ||
|
||
def on_size(self, instance, value): | ||
if value[0] < 1 or value[1] < 1: | ||
return | ||
self.fbo.size = value | ||
self.fbo_texture = self.fbo.texture | ||
|
||
def on_vs(self, instance, value): | ||
self.canvas.shader.vs = value | ||
|
||
def on_fs(self, instance, value): | ||
self.canvas.shader.fs = value | ||
|
||
def update_shader(self, dt): | ||
self.canvas['projection_mat'] = Window.render_context['projection_mat'] | ||
self.canvas['modelview_mat'] = Window.render_context['modelview_mat'] | ||
self.canvas['alpha'] = float(self.alpha) | ||
self.canvas['size'] = map(float, self.size) | ||
|
||
class Slides(FloatLayout): | ||
container1 = ObjectProperty(None) | ||
container2 = ObjectProperty(None) | ||
old_index = NumericProperty(-1) | ||
index = NumericProperty(0) | ||
scroll_x = NumericProperty(0) | ||
time = NumericProperty(0) | ||
max_index = NumericProperty(0) | ||
|
||
def __init__(self, **kwargs): | ||
self.slides = [] | ||
super(Slides, self).__init__(**kwargs) | ||
Window.bind(on_keyboard=self.on_keyboard) | ||
Clock.schedule_interval(self.increase_time, 1 / 30.) | ||
Clock.schedule_once(self.init, 0) | ||
|
||
def init(self, dt): | ||
index = self.index | ||
self.index = -1 | ||
self.index = index | ||
|
||
def increase_time(self, dt): | ||
self.time += dt | ||
|
||
def on_keyboard(self, instance, scancode, *largs): | ||
# page down | ||
if scancode == 281: | ||
self.old_index = self.index | ||
for index in xrange(self.index+1, self.max_index): | ||
slide = self.slides[index] | ||
if slide.is_section: | ||
self.index = index | ||
return True | ||
return True | ||
# page up | ||
if scancode == 280: | ||
self.old_index = self.index | ||
for index in xrange(self.index-1, -1, -1): | ||
slide = self.slides[index] | ||
if slide.is_section: | ||
self.index = index | ||
return True | ||
return True | ||
# home | ||
if scancode == 278: | ||
self.old_index = self.index | ||
self.index = 0 | ||
return True | ||
# end | ||
if scancode == 279: | ||
self.old_index = self.index | ||
self.index = self.max_index - 1 | ||
return True | ||
# left key | ||
if scancode == 275: | ||
self.old_index = self.index | ||
self.index = min(self.max_index - 1, self.index + 1) | ||
return True | ||
# right key | ||
if scancode == 276: | ||
self.old_index = self.index | ||
self.index = max(0, self.index - 1) | ||
return True | ||
|
||
def add_widget(self, widget): | ||
if isinstance(widget, Slide): | ||
self.slides.append(widget) | ||
self.max_index = len(self.slides) | ||
return | ||
return super(Slides, self).add_widget(widget) | ||
|
||
def on_index(self, instance, value): | ||
index = self.index | ||
old_index = self.old_index | ||
if index == old_index: | ||
return | ||
self.container2.clear_widgets() | ||
self.container2.add_widget(self.slides[index]) | ||
d = 1. if index > old_index else -1. | ||
self.container2.alpha = d | ||
Animation(alpha=0., d=.3, t='out_quad').start(self.container2) | ||
self.slides[index].active = True | ||
if old_index != -1: | ||
self.container1.clear_widgets() | ||
self.container1.add_widget(self.slides[old_index]) | ||
self.container1.alpha = 0. | ||
Animation(alpha=-d, d=.3, t='out_quad').start(self.container1) | ||
self.slides[old_index].active = False | ||
|
||
class SlidesViewer(App): | ||
def build(self): | ||
filename = self.options['filename'] | ||
if isdir(filename): | ||
directory = filename | ||
filename = join(filename, 'presentation.kv') | ||
else: | ||
directory = dirname(filename) | ||
sys.path += [directory] | ||
resource_add_path(directory) | ||
return Builder.load_file(filename) | ||
|
||
Factory.register('SlideShaderContainer', cls=SlideShaderContainer) | ||
Factory.register('Slides', cls=Slides) | ||
Factory.register('SlidesBackground', cls=SlidesBackground) | ||
Factory.register('SlidesForeground', cls=SlidesForeground) | ||
Factory.register('Slide', cls=Slide) | ||
|
||
if __name__ == '__main__': | ||
import sys | ||
if len(sys.argv) != 2: | ||
print 'Usage: main.py <filename.kv>' | ||
sys.exit(1) | ||
SlidesViewer(filename=sys.argv[1]).run() |