Skip to content

Instantly share code, notes, and snippets.

@JMV38
Created August 18, 2019 11:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JMV38/1b67ba85ca7bd7b23c7058216895372c to your computer and use it in GitHub Desktop.
Save JMV38/1b67ba85ca7bd7b23c7058216895372c to your computer and use it in GitHub Desktop.
appli.py
import ui, photos, console, json, copy, math, io, os, dialogs, webbrowser
from PIL import Image
from numpy import clip
version = 9
# constants
TOP = 'TOP'
PHOTOS1 = 'PHOTOS'
PHOTOS2 = 'TEMP'
EDITOR = 'EDITOR'
PAGES = 'PAGES'
tw = None
def getAssets():
suffix1 = '_pages_and_photos'
suffix2 = '_pages'
lst = photos.get_albums()
# a dictionnary to acces albums via their title
allAlbums = dict()
for album in lst:
allAlbums.update({album.title:album})
# the usable albums are the ones i have not created
inputAlbums = dict()
for title in allAlbums.keys():
if title[-len(suffix1):] != suffix1 and title[-len(suffix2):] != suffix2:
inputAlbums.update({title:allAlbums[title]})
# choose the album uo use
inputTitle = dialogs.list_dialog('Select an album', list(inputAlbums.keys()))
inputAlbum = allAlbums[inputTitle]
# get the output albums (create it if it does not exist)
outputTitle1 = inputTitle + suffix1
try: outputAlbum1 = allAlbums[outputTitle1]
except: outputAlbum1 = photos.create_album(outputTitle1)
outputTitle2 = inputTitle + suffix2
try: outputAlbum2 = allAlbums[outputTitle2]
except: outputAlbum2 = photos.create_album(outputTitle2)
# get assets
inputAssets = list(inputAlbum.assets)
outputAssets = list(outputAlbum1.assets)
pageAssets = list(outputAlbum2.assets)
# remove from input the assets already used
for asset in outputAssets:
if asset in inputAssets: inputAssets.remove(asset)
return inputAssets, pageAssets, outputAlbum1, outputAlbum2
def save(page, outputAlbum1, outputAlbum2, thumbs):
if len(thumbs) == 0:
console.hud_alert('nothing to save', 'error')
return
# save page image in pythonista
path = 'temp.jpg'
with ui.ImageContext(page.width, page.height) as ctx:
page.draw_snapshot()
ui_image = ctx.get_image()
pil = Image.open(io.BytesIO(ui_image.to_png()))
crop_pil = pil
crop_pil.save(path , quality=95)
# save page image in albums
asset = photos.create_image_asset(path)
os.remove(path)
outputAlbum1.add_assets([asset])
outputAlbum2.add_assets([asset])
photos2.add(Thumb(bg,asset,100))
photos2.layout()
# save used photos in albums
for thumb in thumbs:
asset = thumb.asset
if asset in outputAlbum1.assets : outputAlbum1.remove_assets([asset])
outputAlbum1.add_assets([asset])
thumb.saved = True
console.hud_alert('saved')
page.saved = True
class Splitter(ui.View):
# an invisible touch sensor to adjust the edge of containers
def __init__(self, name, parentView, y, ymin, ymax, **kvargs):
super(Splitter,self).__init__(**kvargs)
self.name = name
self.moving = EventHook(y='new center line y position') # the event to manage movement
w,h = ui.get_screen_size()
self.frame = (w-100, 0, 80, 30)
self.yc, self.ymin, self.ymax = y, ymin, ymax # the center of the view is used to keep it simple
self.setPosition(y)
self.background_color = (0.5, 0.5, 0.5, 0.4) # change alpha to 0.2 to see sensor
self.border_width = 1
self.border_color = 0
parentView.add_subview(self)
def setPosition(self, y):
self.yc = clip(y, self.ymin, self.ymax) # make sure the center position is within its bounds
self.y = self.yc - self.height/2 # now move the splitter
def moveCenter(self, dy):
self.setPosition(self.yc + dy)
def touch_moved(self, touch):
dx,dy = touch.location - touch.prev_location
yc = self.yc + dy
yc = clip(yc, self.ymin, self.ymax)
dy = yc - self.yc
self.moveCenter(dy) # move the splitter
self.moving(dy) # tell the world
finger.moveToTouch(touch, self)
def touch_began(self, touch): finger.moveToTouch(touch, self)
def touch_ended(self, touch): finger.moveToTouch(touch, self)
def infoPrint(txt):
if tw: print(txt)
class Container(ui.View):
# a horizontal container
def __init__(self, *args, **kvargs):
self.ready = False # ready is used for touch and layout
self.setup(*args, **kvargs) # call .setup(...) in the suclasses instead of .__init__
bg.drop += self.receive
self.ready = True
self.layout()
def setup(self, name, parentView=None, y=None, h=None, **kvargs):
super(Container,self).__init__(**kvargs)
self.background_color, self.border_width, self.border_color = (0.3,0.3,0.3,0.5), 1, 0
self.name = name
w0,h0= ui.get_screen_size()
self.frame = (0, y or 0, w0, h or h0)
if parentView: parentView.add_subview(self)
self.ThumbContainer = self
self.ThumbClass = Thumb
self.thumbs = []
self.grid = False
def moveLowerEdge(self, dy):
self.height += dy
def moveUpperEdge(self, dy):
self.height -= dy
self.y += dy
def add(self, thumb, pos=-1):
#self.info(thumb, 'add')
self.xyToLocal(thumb)
if self.ThumbClass and not type(thumb) == self.ThumbClass:
newThumb = self.ThumbClass(self, thumb.asset, thumb.height, name=thumb.name)
newThumb.frame = thumb.frame
thumb.close()
thumb = newThumb
else:
thumb.top = self
self.thumbs.insert(pos, thumb)
self.ThumbContainer.add_subview(thumb)
thumb.bring_to_front()
return thumb
#self.info(thumb, 'added')
def info(self, thumb, txt):
if tw: print('{} {} {}: at x,y = {} , {}'.format( self.name, txt, thumb.name, thumb.x, thumb.y))
def printSuperviewName(self,thumb):
name = None
ct = thumb
while not isinstance(ct, Container):
try: ct = ct.superview
except: break
try: name = ct.name
except: pass
infoPrint('{} is in view {}'.format(thumb.name, name))
def getThumbLocalOffset(self):
ox = self.x - bg.x
oy = self.y - bg.y
return ox,oy
def xyToLocal(self, thumb):
ox,oy = self.getThumbLocalOffset()
thumb.x -= ox
thumb.y -= oy
def xyToGlobal(self, thumb):
ox,oy = self.getThumbLocalOffset()
thumb.x += ox
thumb.y += oy
def remove(self, thumb):
#self.info(thumb,'remove')
if thumb in self.thumbs: self.thumbs.remove(thumb)
# note: no need to remove from subviews: this is done by add_view(thumb)
self.removeAnimation(thumb)
def removeAnimation(self,thumb):
pass
def delete(self, thumb):
#self.info(thumb,'delete')
if thumb in self.thumbs: self.thumbs.remove(thumb)
if thumb in self.ThumbContainer.subviews: self.ThumbContainer.remove_subview(thumb)
def layout(self):
if not self.ready: return
pass
# for drag and drop
def isOnMe(self, thumb):
ycenter = thumb.y + thumb.height/2
y0, y1 = self.y, self.y+self.height
return (y0 <= ycenter) and (ycenter < y1)
def isBelowMe(self, thumb):
ycenter = thumb.y + thumb.height/2
return (self.y+self.height <= ycenter)
def isAboveMe(self, thumb):
ycenter = thumb.y + thumb.height/2
return (ycenter < self.y)
# note: with send, drop and receive in the container, the thumb never has to know about bg
def sendToOverlay(self, thumb):
if self != bg : self.remove(thumb)
self.xyToGlobal(thumb)
#self.info(thumb, 'send')
bg.receive(thumb) # bg is the only receiver => not event needed here
thumb.bring_to_front()
def dropFromOverlay(self, thumb):
#self.info(thumb, 'drop')
bg.drop(thumb) # use an event because several containers may receive
def receive(self,thumb):
# check if the thumb is for me
if not self.isOnMe(thumb): return
#self.info(thumb, 'receive')
self.add(thumb)
bg.remove(thumb)
class Overlay(Container):
def __init__(self, *args, **kvargs):
self.ready = False
super(Overlay,self).setup(*args, **kvargs)
self.background_color = 0.1
self.ThumbClass = None # because the overlay receives any type
self.drop = EventHook()
self.grid = False
self.ready = True
self.layout()
def receive(self,thumb):
# check if the thumb is for me
if not self.isOnMe(thumb): return
#self.info(thumb, 'receive')
self.add(thumb)
class PageEditor(Container):
def __init__(self, *args, **kvargs):
self.ready = False
super(PageEditor,self).setup(*args, **kvargs)
self.pageSetup()
self.ThumbContainer = self.page
self.ThumbClass = EditorThumb
bg.drop += self.receive
self.ready = True
self.grid = True
self.layout()
def clear(self):
thumbs = list(self.thumbs)
i0 = photos1.getFirstVisibleThumbIndex()
for thumb in thumbs:
if thumb.saved : self.delete(thumb)
else: photos1.add(thumb, i0)
def anim(): photos1.layout()
ui.animate(anim,0.3)
def pageSetup(self):
page = ui.View()
page.w0, page.h0 = 56.3, 21.3 # define your page relative dimensions here
page.aspectRatio = page.w0 / page.h0
page.background_color = 0.1
sep = ui.View(background_color=0.2)
page.add_subview(sep)
page.sep = sep
page.saved = False
self.page = page
self.add_subview(self.page)
def layout(self):
if not self.ready: return
w = self.width
h = w / self.page.aspectRatio
y = (self.height - h)/2 # the page is centered in the frame
self.page.frame = (0, y, w, h)
self.page.sep.frame = (w/2-1, 0, 2, h)
def getThumbLocalOffset(self):
ox = self.x + self.ThumbContainer.x - bg.x
oy = self.y + self.ThumbContainer.y - bg.y
return ox,oy
def receive(self,thumb):
# check if the thumb is for me
if not self.isOnMe(thumb): return
#self.info(thumb, 'receive')
# 1/ find where to drop it
ox,oy = self.getThumbLocalOffset()
xDrop0 = thumb.x - ox
yDrop0 = thumb.y - oy
yDrop1 = max(yDrop0, 0)
yDrop1 = min(yDrop1, self.ThumbContainer.height - thumb.height)
# 2/ insert it with a smooth animatiom
def align():
thumb.ready = False
thumb.y += yDrop1-yDrop0
def setReady():
newThumb = self.add(thumb)
#newThumb.setSizeFromH(thumb.height)
newThumb.ready = True
if yDrop0!=yDrop1:
ui.animate(animation = align, duration = 0.2, completion = setReady)
else:
setReady()
bg.remove(thumb)
class PhotoPicker(Container):
def __init__(self, name, parentView, y, h, step=0, **kvargs):
self.ready = False
super(PhotoPicker,self).setup(name, parentView, y, h, **kvargs)
self.scrollviewSetup(h)
self.ThumbContainer = self.sv
self.ThumbClass = PhotoThumb
bg.drop += self.receive
self.step = step
self.ready = True
self.layout()
def scrollviewSetup(self, h):
self.sv = ui.ScrollView()
self.sv.background_color = 0.5
self.add_subview(self.sv)
self.enableScrollviewToInterceptTouches(True)
def enableScrollviewToInterceptTouches(self, boolean):
self.sv.scroll_enabled = boolean
def getFirstVisibleThumbIndex(self):
i0=-1
for i in range(len(self.thumbs)):
if self.thumbs[i].x > self.sv.content_offset.x:
i0=i
break
return i0
def initAssets(self, assets):
self.assets = assets
nbmax = 200
nbmax = min(nbmax, len(self.assets))
xc,yc = self.width/2, self.height/2
label = ui.Label(x=xc-100, y=yc-20, width=200, height=40, text_color='lightgreen')
self.add_subview(label)
self.ThumbContainer.hidden = True
for i in range(0,nbmax):
label.text = 'loading image {}'.format(i)
thumb = PhotoThumb(self, self.assets[i], self.height, name='thumb {}'.format(i))
self.thumbs.append(thumb)
self.ThumbContainer.add_subview(thumb)
self.remove_subview(label)
self.ThumbContainer.hidden = False
self.layout()
def getThumbLocalOffset(self):
ox = self.x + self.ThumbContainer.x - self.sv.content_offset.x - bg.x
oy = self.y + self.ThumbContainer.y - bg.y
return ox,oy
def getAlbum(self):
title = 'bretagne'
album = False
for a in photos.get_albums():
if a.title == title: album = a; break
if not album:
console.alert('album {} does not exist'.format(title))
return album.assets
def layout(self): # called automatically when the view size changes
if not self.ready: return
# 1/ adjust and realign thumbs
thumbs = self.thumbs
x = 0
for thumb in thumbs:
thumb.setSizeFromH(self.height)
#thumb.height = self.height
thumb.x = x
thumb.y = 0
x = x + thumb.width + self.step
# 2/ adjust scrollview
self.sv.content_size = (x,self.height)
# adjust content offset so the center doesnt move despite the resize
w0 = self.width/2
x0 = self.sv.content_offset.x + w0
zoom = self.height / self.sv.height
self.sv.content_offset = (x0 * zoom - w0, 0)
# define the scrollview size at the end!!
self.sv.frame = (0, 0, self.width, self.height)
def removeAnimation(self,thumb):
def anim(): self.layout()
ui.animate(anim,0.3)
def sendToOverlay(self, thumb):
self.xyToGlobal(thumb)
if self != bg : self.remove(thumb)
bg.receive(thumb) # bg is the only receiver => not event needed here
thumb.bring_to_front()
self.enableScrollviewToInterceptTouches(False)
def dropFromOverlay(self, thumb):
#self.info(thumb, 'drop')
bg.drop(thumb) # use an event because several containers may receive
self.enableScrollviewToInterceptTouches(True)
def receive(self,thumb):
# check if the thumb is for me
if not self.isOnMe(thumb): return
#self.info(thumb, 'receive')
# 1/ find where to insert it
ox,oy = self.getThumbLocalOffset()
xDrop = thumb.x - ox
thumbs = self.thumbs
iDrop = len(thumbs)
for i in range(len(thumbs)):
xEnd = thumbs[i].x + thumbs[i].width
if xEnd > xDrop:
if xDrop < thumbs[i].x + thumbs[i].width /2 : iDrop = i
else : iDrop = i+1
break
# 2/ insert it with a smooth animatiom
def align():
thumb.ready = False
newThumb = self.ThumbClass(self, thumb.asset, self.height, name=thumb.name)
newThumb.hidden = True
self.thumbs.insert( iDrop, newThumb)
self.ThumbContainer.add_subview(newThumb)
self.layout()
self.xyToGlobal(newThumb)
thumb.iv.flex = 'WH'
thumb.frame = newThumb.frame
self.xyToLocal(newThumb)
self.newThumb = newThumb
def setReady():
thumb.close()
self.newThumb.hidden = False
self.newThumb.ready = True
ui.animate(animation = align, duration = 0.3, completion = setReady)
bg.remove(thumb)
class Thumb(ui.View):
# a view and an imageView inside, remember its asset
def __init__(self, top, asset, h, **kvargs):
self.ready = False
super(Thumb,self).__init__(**kvargs)
self.border_color, self.border_width, self.top, self.asset = 'black', 1, top, asset
self.ivSetup(asset)
self.aspectRatio = self.iv.aspectRatio # initially this is true, then it may change (crop)
self.setSizeFromH(h)
w1,h1 = self.iv.imgSize
self.scale = self.height / h1
self.iv.scale = self.scale
self.dragging = False
self.ready = True
self.saved = False
self.layout() # nb: ready must be true!
def ivSetup(self, asset):
img = self.getImage(asset)
iv = ui.ImageView()
iv.image = img
iv.imgSize = img.size
w1, h1= iv.imgSize
iv.aspectRatio = w1 / h1
self.iv = iv
self.add_subview(self.iv)
def getImage(self, asset): return self.getImageSmall(asset)
def getImageSmall(self, asset): return self.getImageHeight(asset, 200)
def getImageBig(self, asset): return self.getImageHeight(asset, 600)
def getImageHeight(self, asset, h):
w0, h0 = asset.pixel_width , asset.pixel_height
if h>h0: w,h = w0,h0
else: w = int(h * w0 / h0)
img = asset.get_ui_image((w,h)).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
return img
def layout(self): # needed for image crop
if not self.ready: return # just to avoid errors while initializing
self.iv.frame = (0, 0, self.width, self.height)
def setSizeFromH(self, h):
try:
if type(self)==EditorThumb: self.infoPrint('selfSizeFromH')
except: pass
self.height = h
self.width = int(h * self.aspectRatio)
def info(self, txt):
if tw: print('{} {} from {} at x,y = {} , {}'.format(
self.name, txt, self.top.name, self.x, self.y))
def infoPrint(self, txt):
if tw: print(txt)
# these actions send, drag, drop CAN be modified by subclasses
def send(self, touch):
self.top.sendToOverlay(self)
def drag(self, touch):
dx,dy = self.getTouch_dxdy(touch)
self.move(dx,dy)
def drop(self, touch):
self.top.dropFromOverlay(self)
def close(self):
self.top.delete(self)
def horizontalMove(self, touch):
dx,dy = self.getTouch_dxdy(touch)
if self.width < bg.width : return
x = self.x + dx
x = min( x , 0)
x = max( x , bg.width - self.width)
self.x = x
def doNothing(self, touch): pass
# these low level actions should NOT be modified by subclasses
def touch_began(self, touch):
if not self.ready: return
self.infoPrint('')
self.info('touch began')
self.touch0 = touch.location
self.send(touch)
menu.selectThumb(self)
finger.moveToTouch(touch, self)
def touch_ended(self, touch):
if not self.ready: return
self.touch0 = None
self.drop(touch)
finger.moveToTouch(touch, self)
def touch_moved(self, touch):
if not self.ready: return
self.drag(touch)
finger.moveToTouch(touch, self)
def getTouch_dxdy(self, touch):
x0,y0 = touch.prev_location
x,y = touch.location
dx,dy = x-x0, y-y0
return dx,dy
def move(self, dx, dy):
self.x += dx
self.y += dy
class HighResThumb(Thumb):
def getImage(self, asset): return self.getImageHeight(asset, 1200)
class PhotoThumb(Thumb):
def send(self, touch):
x,y = touch.location
edge = 0.33
if y < self.height*edge or y > self.height*(1-edge): # move me...
self.dragging = True
self.previousTop = self.top
self.previousTop.enableScrollviewToInterceptTouches(False)
self.top.sendToOverlay(self)
else: # ... or move container
self.dragging = False
self.top.enableScrollviewToInterceptTouches(True)
def drag(self, touch):
if self.dragging:
dx,dy = self.getTouch_dxdy(touch)
self.move(dx,dy)
def drop(self, touch):
if self.dragging:
self.dragging = False
self.top.dropFromOverlay(self)
self.previousTop.enableScrollviewToInterceptTouches(True)
class EditorThumb(Thumb):
def ivSetup(self, asset):
super(EditorThumb,self).ivSetup(asset)
iv = self.iv
w1, h1= iv.imgSize
iv.frame = (0,0,w1,h1)
iv.x = 0
iv.y = 0
iv.imgCrop = (0,0,0,0)
self.codes ={'move':[4], 'resize':[0,2,6,8], 'crop':[1,3,5,7]}
self.scaleChange = 1
# a high resolution image that can be crpped and zoomed
def getImage(self, asset): return self.getImageBig(asset)
def send(self, touch):
self.dragging = False
self.initialTouchCode = self.getTouchCode(touch)
self.top.sendToOverlay(self)
def drag(self, touch):
self.moveOrResizeOrCrop(touch)
def drop(self, touch):
self.top.dropFromOverlay(self)
if self.dragging:
self.dragging = False
def cropLayout(self):
w,h = self.iv.imgSize
l, t, r, b = self.iv.imgCrop
s = self.iv.scale
self.iv.width = self.width + l*s + r*s
self.iv.height = self.height + t*s + b*s
self.iv.x = -l*s
self.iv.y = -t*s
self.iv.scale = self.iv.height / h
def layout(self): # needed for image crop
if not self.ready: return # just to avoid errors while initializing
w,h = self.iv.imgSize
l, t, r, b = self.iv.imgCrop
self.iv.scale *= self.scaleChange
self.scaleChange = 1
s = self.iv.scale
self.iv.width = self.width + l*s + r*s
self.iv.height = self.height + t*s + b*s
self.iv.x = -l*s
self.iv.y = -t*s
def resize(self, dw, dh):
h = self.height
s = (h+dh)/h
self.height *= s
self.width *= s
self.scale *= s
self.scaleChange = s
#self.normalLayout()
def crop(self, dl, dt, dr, db):
# scale is constant
#self.layout = self.cropLayout
s = self.iv.scale
l, t, r, b = self.iv.imgCrop
l, t, r, b = l*s, t*s, r*s, b*s
dl = max(0, l+dl) - l
dt = max(0, t+dt) - t
dr = max(0, r+dr) - r
db = max(0, b+db) - b
self.x += dl
self.y += dt
self.width -= dl + dr
self.height -= dt + db
l, t, r, b = l+dl, t+dt, r+dr, b+db
l, t, r, b = l/s, t/s, r/s, b/s
self.iv.imgCrop = (l, t, r, b)
self.cropLayout()
def minWHchange(self,dw,dh):
w0,h0 = self.width, self.height
# keep aspect ratio
ds = (dw/w0 + dh/h0)/2
dw,dh = w0*ds, h0*ds
dmax = 10 # limit size change
d = max(abs(dw), abs(dh))
if d>dmax:
s = dmax/d
dw, dh = dw*s, dh*s
return dw,dh
def moveOrResizeOrCrop(self, touch):
# the touchcan resize or crop, depending on the touched region
code = self.initialTouchCode
# 0 1 2 this encoding of the touch position makes the tests simpler
# 3 4 5
# 6 7 8
dx,dy = self.getTouch_dxdy(touch)
w,h, s = self.width, self.height, 1
w1,h1 = self.iv.imgSize
# moving touch
if code == 4: # center touch
self.move(dx,dy)
# resizing touches
elif code == 8: # bottom right corner touch
dw, dh = self.minWHchange(dx,dy)
self.resize(dw, dh)
elif code == 2: # top right corner touch
dw, dh = self.minWHchange(dx,-dy)
self.resize(dw, dh)
self.move(0, -dh)
elif code == 6: # bottom left corner touch
dw, dh = self.minWHchange(-dx,dy)
self.move(-dw, 0)
self.resize(dw, dh)
elif code == 0: # top left corner touch
dw, dh = self.minWHchange(-dx,-dy)
self.move(-dw, -dh)
self.resize(dw, dh)
# croping touches
elif code == 1: # top touch
self.crop(dl=0, dt=dy, dr=0, db=0)
elif code == 5: # right touch
self.crop(dl=0, dt=0, dr=-dx, db=0)
elif code == 3: # left touch
self.crop(dl=dx, dt=0, dr=0, db=0)
elif code == 7: # bottom touch
self.crop(dl=0, dt=0, dr=0, db=-dy)
def getTouchCode(self, touch):
# the touchcan resize or crop, depending on the touched region
x,y = touch.location
w,h = self.width, self.height
x0, x1, y0, y1 = min(0.3*w,50) , max(0.7*w,w-50), min(0.3*h,50), max(0.7*h,h-50)
code = ((x0<x)*1 + (x1<x)*1) + ((y0<y)*1 + (y1<y)*1)*3
# 0 1 2 this encoding of the touch position makes the tests simpler
# 3 4 5
# 6 7 8
return code
class Menu(ui.View):
def __init__(self):
self.background_color = 0.5
self.frame = (0,0, bg.width, 50)
self.button_d = 32
self.button_y = 12
self.button_w = 32
self.button_h = 32
self.leftOffset = 0
self.rightOffset = bg.width
self.selectedThumb = None
end_button = self.newButton('left', 'iow:close_circled_32')
end_button.action = self.end_button_action
self.end_button = end_button
info_button = self.newButton('left', 'iow:help_circled_32')
info_button.action = self.info_button_action
self.info_button = info_button
save_button = self.newButton('right', 'iow:ios7_camera_32')
save_button.action = self.save_button_action
self.save_button = save_button
clear_button = self.newButton('right', 'iow:refresh_32')
clear_button.action = self.clear_button_action
self.clear_button = clear_button
self.rightOffset -= 100
bigger_button = self.newButton('right', 'iow:arrow_expand_32')
bigger_button.action = self.bigger_button_action
self.bigger = bigger_button
self.bigger.active = False
image_button = self.newButton('right', 'iow:image_32')
image_button.action = self.bigger_button_action
image_button.flex = 'WH'
self.image_button = image_button
bg.add_subview(self)
def getIcon(self,icon): return ui.Image(icon).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
def newButton(self, side, icon=''):
b = ui.Button()
b.image = self.getIcon(icon)
b.width = self.button_w
b.height = self.button_h
b.y = self.button_y
if side == 'left':
x = self.leftOffset + self.button_d
self.leftOffset += self.button_d + b.width
else:
x = self.rightOffset - self.button_d - b.width
self.rightOffset = x
b.x = x
b.enabled = True
self.add_subview(b)
return b
def end_button_action(self,sender):
finger.moveToButton(self.end_button)
bg.close()
def save_button_action(self,sender):
finger.moveToButton(self.save_button)
save(editor.page, outputAlbum1, outputAlbum2, editor.thumbs)
def clear_button_action(self,sender):
finger.moveToButton(self.clear_button)
editor.clear()
def bigger_button_action(self,sender):
finger.moveToButton(self.bigger)
if not self.selectedThumb: return
if self.bigger.active:
self.bigger.active = False
self.bigger.image = self.getIcon('iow:arrow_expand_32')
self.bigThumb.close()
else:
self.bigger.active = True
self.bigger.image = self.getIcon('iow:arrow_shrink_32')
thumb = HighResThumb(bg, self.selectedThumb.asset, h = bg.height-photos2.y)
thumb.send = thumb.doNothing
thumb.drag = thumb.horizontalMove
thumb.drop = thumb.doNothing
thumb.y = photos2.y
thumb.x = bg.width / 2 - thumb.width / 2
bg.add(thumb)
self.bigThumb = thumb
def info_button_action(self, sender):
url = 'https://youtu.be/zs38yqBNbwg'
#webbrowser.open(url, modal=True)
webbrowser.get('safari').open(url)
def selectThumb(self, thumb):
self.selectedThumb = thumb
b = self.image_button
b.image = None
img = thumb.getImageHeight(thumb.asset, 32)
b.background_image = img
w,h = img.size
b.width = w / h * b.height
b.x = self.bigger.x - b.width - self.button_d
def Finger(bg):
finger = ui.ImageView()
iconeOff = 'emj:Raised_Hand'
iconeOn = 'emj:Index_Finger_Up_1'
finger.imageOff= ui.Image(iconeOff).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
finger.imageOn= ui.Image(iconeOn).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
finger.image = finger.imageOff
finger.frame = (0, 0, 100, 100)
finger.alpha = 0
finger.touch_enabled = False
def moveToButton(b):
if finger.alpha == 0: return
x0,y0 = 16,16
x = b.x - 40 + x0
y = b.y - 10 + y0
def move() :
finger.move(x,y)
finger.alpha = 1
def press():
finger.image = finger.imageOn
ui.delay(up, 0.5)
def up():
finger.alpha = 0.5
finger.image = finger.imageOff
ui.animate(move, 0.2, 0, press)
finger.moveToButton = moveToButton
def moveToTouch(touch, thumb):
if finger.alpha == 0: return
try: x0,y0 = thumb.top.getThumbLocalOffset()
except: x0,y0 = 0,0
x,y = touch.location
x = x - 40 + x0 + thumb.x
y = y - 10 + y0 + thumb.y
if touch.phase == 'moved': finger.move(x,y)
if touch.phase == 'began':
def anim() :
finger.move(x,y)
finger.alpha = 1
def finish():
finger.image = finger.imageOn
ui.animate(anim, 0.2, 0, finish)
if touch.phase == 'ended':
def anim() :
#finger.move(bg.width-100, bg.height/3)
finger.alpha = 0.5
ui.animate(anim, 0.2)
finger.image = finger.imageOff
finger.bring_to_front()
finger.moveToTouch = moveToTouch
def move(x,y):
finger.x = x
finger.y = y
finger.move = move
bg.add_subview(finger)
move(bg.width/2, bg.height/2)
return finger
class App():
def __init__(self):
# global events
#cloud = EventHook()
# app top view
global bg, pages, editor, photos1, photos2, menu
bg = Overlay(TOP, background_color='gray')
#bg.drag = EventHook() # when an object starts being dragged
#bg.drop = EventHook() # when an object is dropped
bg.present(hide_title_bar=True)
# for tests
global tw
# the photo containers
w,h = ui.get_screen_size()
photos2 = PhotoPicker(PHOTOS2, bg, 50, 100, step=4)
editor = PageEditor( EDITOR, bg, 150, h-300)
photos1 = PhotoPicker(PHOTOS1, bg, h-150, 150)
#photos1 = PhotoPicker(PHOTOS1, bg, h-300, 150)
#photos2 = PhotoPicker(PHOTOS2, bg, h-150, 150)
global outputAlbum1, outputAlbum2
photoAssets, pageAssets, outputAlbum1, outputAlbum2 = getAssets()
photos1.initAssets(photoAssets)
photos2.initAssets(pageAssets)
# create sensors between containers and link their position to container edges
topSplitter = Splitter('topSplitter', bg, editor.y, ymin=100, ymax=h-100)
botSplitter = Splitter('botSplitter', bg, photos1.y, ymin=100, ymax=h-100)
topSplitter.moving += photos2.moveLowerEdge
topSplitter.moving += editor.moveUpperEdge
botSplitter.moving += editor.moveLowerEdge
botSplitter.moving += photos1.moveUpperEdge
menu = Menu()
global finger
finger = Finger(bg)
#finger.alpha = 1 # activates the finger
#tw = TextWindow((0, 10, 400, 700))
if tw: tw.bring_to_front()
class TextWindow(ui.View):
def __init__(self, frame):
self.touch_enabled = False
self.frame = frame
self.background_color = (0,0,0,0)
console_view = ui.TextView(name='console_view')
self.lines = []
#console_view = ui.ListDataSource(self.lines)
self.console_view = console_view
console_view.border_width = 1
console_view.frame = self.frame
console_view.flex = 'wh'
console_view.background_color = (0,0,0, 0.2)
console_view.text_color = 'lightgreen'
self.add_subview(console_view)
bg.add_subview(self)
global print
print = self.print
console.print = self.print
print('console ready')
def print(self,txt):
lines = self.lines
lines.append(txt)
if len(lines)>40:
lines.remove(lines[0])
str = ''
for txt in lines: str += txt+'\n'
self.console_view.text = str
class EventHook(object):
def __init__(self, **kwargs):
self.documentation = kwargs # named parameters should be: param='explaination'
self.keys = kwargs.keys() # the parameters should be named and defined clearly
self.__handlers = []
def __iadd__(self, handler):
self.__handlers.append(handler)
return self
def __isub__(self, handler):
self.__handlers.remove(handler)
return self
def __call__(self, *args, **kwargs):
for key in kwargs.keys():
if not key in self.keys:
print('{} is not a valid parameter'.format(key))
for handler in self.__handlers:
handler(*args, **kwargs)
def removeObject(self, inObject):
# for bound methods obj.meth, remove it via 'obj' (usefull when deleting an object)
for theHandler in self.__handlers:
try:
# note pyhton2: theHandler.im_self
# note pyhton3: theHandler.__self__
if theHandler.__self__ == inObject:
self -= theHandler
except:
pass
if __name__ == "__main__":
console.clear()
app = App()
test = False
if test:
thumb1= Thumb(bg, photos1.thumbs[0].asset, x=400, y=0 , name='thumb1')
photos2.receive(thumb1)
thumb2 = Thumb(bg, photos1.thumbs[4].asset, x=400, y=200 , name='thumb2')
bg.receive(thumb2)
thumb3 = EditorThumb(bg, photos1.thumbs[2].asset, x=400, y=400, name='thumb3')
editor.receive(thumb3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment