Created
August 18, 2019 11:22
-
-
Save JMV38/1b67ba85ca7bd7b23c7058216895372c to your computer and use it in GitHub Desktop.
appli.py
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
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