Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Cover Flow view
-
Thanks @JonB. Right now I'm trying to write a function for the x positions of the imageviews based on the length of the stack and their position in the stack
-
@Webmaster4o, I think the I VirtualView Class am working on , could be helpful, even though its slanted for vertical display, but could be easily modified for horizontal display!
worth a look.
https://github.com/Phuket2/VirtualView.git -
All the examples I can find online rely HEAVILY on OpenGL. :|
-
People have tried to do this in the past with pygame/pyopenGL, I'm gonna look at some of those examples. I got a UI set up:
The UI is the width of my iPad mini screen, and half the height. I'll setup ananimate function that scales and moves each ImageView to the position of the next. Then replace that view with the original, but all the images are shifted, so there is no jump, but the layout of the imageviews is the same. -
Looking forward to seeing more!
-
Translated pyui to code, images in place. Have a commitment now, so I possibly won't be back for 4 hours.
# coding: utf-8 import ui from PIL import Image from io import BytesIO def pil_to_ui(img): b = BytesIO() img.save(b, "PNG") data = b.getvalue() b.close() return ui.Image.from_data(data) class CoverFlow(ui.View): def __init__(self, images): self.images = [pil_to_ui(image) for image in images] self.frame = (0, 0, 1024, 384) self.background_color='#0F0F0F' #Create subviews iv0 = ui.ImageView(frame=(-100, 142, 100, 100), name='iv0') iv0.image = self.images[0] self.add_subview(iv0) iv1 = ui.ImageView(frame=(-10, 122, 140, 140), name='iv1') iv1.image = self.images[1] self.add_subview(iv1) iv2 = ui.ImageView(frame=(90, 102, 180, 180), name='iv2') iv2.image = self.images[2] self.add_subview(iv2) iv3 = ui.ImageView(frame=(180, 72, 240, 240), name='iv3') iv3.image = self.images[3] self.add_subview(iv3) iv4 = ui.ImageView(frame=(327, 7, 370, 370), name='iv4') iv4.image = self.images[4] self.add_subview(iv4) iv5 = ui.ImageView(frame=(604, 72, 240, 240), name='iv5') iv5.image = self.images[5] self.add_subview(iv5) iv5.send_to_back() iv6 = ui.ImageView(frame=(754, 102, 180, 180), name='iv6') iv6.image = self.images[6] self.add_subview(iv6) iv6.send_to_back() iv7 = ui.ImageView(frame=(894, 122, 140, 140), name='iv7') iv7.image = self.images[7] self.add_subview(iv7) iv7.send_to_back() iv8 = ui.ImageView(frame=(1024, 142, 100, 100), name='iv8') iv8.image = self.images[8] self.add_subview(iv8) iv8.send_to_back() def did_load(self): self.images = [s.image for s in self.subviews] if __name__ == '__main__': names = ['test:Boat', 'test:Lenna', 'test:Pythonista', 'test:Peppers', 'test:Sailboat', 'test:Bridge', 'test:Mandrill', 'emj:Baby_Chick_3', 'emj:Tulip'] images = [Image.open(n) for n in names] view = ui.View(background_color='#0f0f0f') view.add_subview(CoverFlow(images)) view.present(hide_title_bar=True)
-
Started animation. Somehow the first and last images end up stacked during animation, so that needs to be fixed.
-
Stopping for tonight. First day of high school tomorrow, that means summer's over and I have a lot less time. Here's what I have now. Fixed most bugs. I still want to tweak the animation, but here it is.
# coding: utf-8 import ui from PIL import Image from io import BytesIO def pil_to_ui(img): b = BytesIO() img.save(b, "PNG") data = b.getvalue() b.close() return ui.Image.from_data(data) class CoverFlow(ui.View): def __init__(self, images): self.images = [pil_to_ui(image) for image in images] self.frame = (0, 0, 1024, 384) self.background_color='#0F0F0F' #Create subviews iv0 = ui.ImageView(frame=(-100, 142, 100, 100), name='iv0') iv0.image = self.images[0] iv0.prev_frame = iv0.frame self.add_subview(iv0) iv1 = ui.ImageView(frame=(-10, 122, 140, 140), name='iv1') iv1.image = self.images[1] iv1.prev_frame = iv1.frame self.add_subview(iv1) iv2 = ui.ImageView(frame=(90, 102, 180, 180), name='iv2') iv2.image = self.images[2] iv2.prev_frame = iv2.frame self.add_subview(iv2) iv3 = ui.ImageView(frame=(180, 72, 240, 240), name='iv3') iv3.image = self.images[3] iv3.prev_frame = iv3.frame self.add_subview(iv3) iv4 = ui.ImageView(frame=(327, 7, 370, 370), name='iv4') iv4.image = self.images[4] iv4.prev_frame = iv4.frame self.add_subview(iv4) iv5 = ui.ImageView(frame=(604, 72, 240, 240), name='iv5') iv5.image = self.images[5] self.add_subview(iv5) iv5.prev_frame = iv5.frame iv5.send_to_back() iv6 = ui.ImageView(frame=(754, 102, 180, 180), name='iv6') iv6.image = self.images[6] self.add_subview(iv6) iv6.prev_frame = iv6.frame iv6.send_to_back() iv7 = ui.ImageView(frame=(894, 122, 140, 140), name='iv7') iv7.image = self.images[7] self.add_subview(iv7) iv7.prev_frame = iv7.frame iv7.send_to_back() iv8 = ui.ImageView(frame=(1024, 142, 100, 100), name='iv8') iv8.image = self.images[8] self.add_subview(iv8) iv8.prev_frame = iv8.frame iv8.send_to_back() def right_anim(self): for sub in self.subviews: index = self.subviews.index(sub) next = self.subviews[index+1] if index<8 else self.subviews[0] sub.frame = next.prev_frame for sub in self.subviews: sub.prev_frame = sub.frame #Reorder layers based on the fact that biggest images are in front #Bring smallest images to front first for s in sorted(self.subviews, key=lambda x: x.frame[3]): s.bring_to_front() def left_anim(self): for sub in self.subviews: index = self.subviews.index(sub) next = self.subviews[index-1] if index>0 else self.subviews[8] sub.frame = next.prev_frame for sub in self.subviews: sub.prev_frame = sub.frame #Reorder layers based on the fact that biggest images are in front #Bring smallest images to front first for s in sorted(self.subviews, key=lambda x: x.frame[3]): s.bring_to_front() def touch_began(self, touch): pass def touch_moved(self, touch): if touch.location[0]<touch.prev_location[0]: self.touch_direction = 0 elif touch.location[0]>touch.prev_location[0]: self.touch_direction = 1 def touch_ended(self, touch): if self.touch_direction: ui.animate(self.right_anim, duration=1.0) else: ui.animate(self.left_anim, duration=1.0) if __name__ == '__main__': names = ['test:Boat', 'test:Lenna', 'test:Pythonista', 'test:Peppers', 'test:Sailboat', 'test:Bridge', 'test:Mandrill', 'emj:Baby_Chick_3', 'emj:Tulip'] images = [Image.open(n) for n in names] view = ui.View(background_color='#0f0f0f') view.add_subview(CoverFlow(images)) view.present(hide_title_bar=True)
-
@Webmaster4o , very nice. Only thing I would say is the animate time. I am on a iPad Air 2 and it did not feel responsive enough. I changed your values to from 1.0 to 0.5, a lot nicer for animation experience. Maybe a nice addition would be to look at the hardware to set the animation delay. Gives me the idea to do the same also for some of the stuff I am trying to write.
-
Yeah. It still needs work in the following areas:
- It only works with exactly 9 images, it should work with as many as you need
- You can see the last image sliding behind to the first
- It only supports the screen size of my iPad mini
But, I'm going to school now
-
Yeah, I can see needs a few things done. But still good. I will try to get into a loop handling 1 to x images. Try that is :)
Also use layout so that its adjusts to screen size and orientation. -
A simplification of
pil_to_ui()
and a simplification ofCoverFlow.__init__()
via the addition ofmake_image_view()
. One of the things that still needs doing is to calculateframes
based on screen size/orientation.def pil_to_ui(img): with BytesIO() as b: img.save(b, "PNG") return ui.Image.from_data(b.getvalue()) def make_image_view(image, frame, index): image_view = ui.ImageView(frame=frame, name='iv{}'.format(index)) image_view.image = image image_view.prev_frame = frame return image_view class CoverFlow(ui.View): def __init__(self, images): self.images = (pil_to_ui(image) for image in images) self.frame = (0, 0, 1024, 384) self.background_color='#0F0F0F' frames = ( (-100, 142, 100, 100), (-10, 122, 140, 140), (90, 102, 180, 180), (180, 72, 240, 240), (327, 7, 370, 370), (604, 72, 240, 240), (754, 102, 180, 180), (894, 122, 140, 140), (1024, 142, 100, 100) ) #Create subviews for i, image in enumerate(self.images): self.add_subview(make_image_view(image, frames[i], i))
-
Combined
left_anim()
andright_anim()
into a singleanim()
method. Tightened uptouch_moved()
andtouch_ended()
to make the ui more responsive. My sense is that Apple's CoverFlow starts to move intouch_moved()
whereas this version does not start to move untiltouch_ended()
.def anim(self): len_subviews = len(self.subviews) for i, sub in enumerate(self.subviews): next = self.subviews[(i + (1 if self.touch_direction else -1)) % len_subviews] sub.frame = next.prev_frame for sub in self.subviews: sub.prev_frame = sub.frame #Reorder layers based on the fact that biggest images are in front #Bring smallest images to front first for s in sorted(self.subviews, key=lambda x: x.frame[3]): s.bring_to_front() def touch_began(self, touch): pass def touch_moved(self, touch): self.touch_direction = int(touch.location.x > touch.prev_location.x) def touch_ended(self, touch): ui.animate(self.anim, duration=0.25)
-
@ccc Like your use of
enumerate
, it reminded me thatenumerate
exists. My code can be so much cleaner with it. -
@ccc, looks very nice. I was also rewriting , but not as good as you. But I was trying to find a a nice mathematical way to derive the frames. Also, you pumped into my head about enumerate, is so handy.
-
@ccc , the first thing that comes to mind for me when I see coverflow, is like a sine wave. Does it make sense to you? But this sort of math is very difficult for me. I left school, too early. I am looking at some nice animations about sin & cos on Wikipedia.
https://en.m.wikipedia.org/wiki/Sine
My idea is that the answer for frames is in this formula. Just asking. Maybe you know. -
I was going to use a quadratic. Going into NYS common core geometry this year, so I've just completed algebra 1. I can easily pull a quadratic function for x values using my graphing calculator, but I don't know how to do this with screen size as a variable. I just positioned the boxes by ear in a pyui, then took their frames and put them into the code.
A quadratic is preferable over a sine, because a sine goes up and down multiple times. Quadratics look like this:
Here x would be ImageView index, and y would be the distance from the previous box. Distances are greatest in the middle, boxes are closer together towards the edges.
What I'll probably end up doing is defining x as a percentage, which represents x value over total screen width. Then on my iPad, the value is x percent of 1024.
-
This post is deleted! -
Ok, attempt one at mathematically describing my frame positions with a quadratic equation didn't go well. This table shows the distances between the centers of frames:
Y1 is the distance between the centers of the first and second frames, Y2 the distance between the centers of the second and third frames, etc.Pulling a quadratic from this data resulted in a horribly messy function with a bad fit. The function I got is
And it's a bad fit. The function plotted over the points:
I'm going back to percentages. I was thinking having it be a smooth rounded function might make the whole thing look more even, but it's beyond my ability -
Look this is way out of my league. But I can still experiment. I did something with sin using rads With Pi * 1(from what I can see this should be 180 deg) The points go from low to 1.0 to low again. The points each side of 1.0 are the same. First in ascending order up to 1 then decending on the other side of 1.0
Look, I don't know what I am doing. Just trying for fun. What I have done so far has flaws in it, I am am still unsure how to control it :) will keep reading though. Just hard without a math background.One example output is. If I get the algo working correctly will post it. Even though I may not be the correct approach. I have a feeling if I understood it better, could prove interesting anyway. cos, could possibly play a role also.