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.
Dynamic views and Images in the ui module
-
I've been trying to create an interface prompts for a Camera Roll pic (photos module) and displays it, which is fairly simple using
ImageView()
andui.Image.from_data(photos.pick_image(raw_data=True))
withui.CONTENT_SCALE_ASPECT_FIT
. The image will properly fit the bounds of the screen and adapt as you rotate the device.My problem has been that I want to create touch events, but
ImageView()
can't handle those, so I assume I have to create a custom View on top of it, however, since I want the touch events to be available solely within the image displayed below, I must make this "touch view" the same size as the "image view".Trying
ImageView.image.size
will give me the original dimensions, while I want the dimensions after theui.content_mode
is applied. My best bet right now is to run a function that calculates the outcome from thecontent_mode
based on theui.get_screen_size()
, however, this one always delivers the dimensions in portrait mode, meanwhile I don't know a way to get the device current orientation.The question is: how to make a View with the same width and height of the image within a sibling ImageView?
Basic structure:
container = ui.View() imageview = ui.ImageView() imageview.image = ui.Image.from_data(photos.pick_image(raw_data=True)) imageview.flex = 'WH' imageview.content_mode = `ui.CONTENT_SCALE_ASPECT_FIT` touchview = ui.View() touchview.width = ? touchview.height = ?
Thank you.
-
I hope this little demo script/workflow makes these things a bit easier to understand:
http://www.editorial-workflows.com/workflow/5688890476199936/idOllauYutA
In general, when you need to resize a view dynamically, the
layout
method in a custom view is a good place to do that because it gets called automatically when you rotate the device, etc. In thelayout
method, you can get the size of the view by using thebounds
(orwidth
,height
) attribute.In the demo, I've used the
layout
method to calculate the scaled size and position of the image (based on the size of the container), and to align an overlay view on top of it with the image underneath. When you touch the overlay, a simple red square shows up under your finger (obviously not very useful, but shows how you can handle touches on the overlay).The
ui.get_screen_size
function isn't really intended to do layout with, the primary purpose is really to find out what kind of device you're running on, and it's intended to always return the same value, regardless of orientation. -
This was a huge step forward, Ole. Thank you. I managed to do most touch events already, but there are a few questions left, which I believe are minor:
On that setup, how do I add ButtonItems to the main view that interact with the touch events created in the
image_view
? For example, right now I managed to draw a square with thetouch_moved
events:def touch_moved(self, touch): loc = touch.location if loc[0] < self.indicator_start.center[0]: self.crop_area.x = loc[0] if loc[1] < self.indicator_start.center[1]: self.crop_area.y = loc[1] self.crop_area.width = abs(loc[0] - self.indicator_start.center[0]) self.crop_area.height = abs(loc[1] - self.indicator_start.center[1])
So far I only managed to add ButtonItems in the main view, but I'm looking to set them as False by default and True only after the
touch_ended
events from theimage_view
. -
You can only add
ButtonItem
s to a view that is "presented" (i.e. the main view), but you could access them from a subview viaself.superview.right_button_items
... -
self.crop_button = ui.ButtonItem(title='Crop', enabled=False)
self.superview.right_button_items = [self.crop_button]
self.clear_button = ui.ButtonItem(title='Clear', enabled=False)
self.superview.left_button_items = [self.clear_button]In the DragView, if i add this to the
__init__
:AttributeError: 'NoneType' object has no attribute 'right_button_items_'
But if I had to the
draw
function it works fine. I wonder if that's an expected behavior. -
Ok, now I'm trying to improve the workflow and add a touch action that allows the user to drag the crop area. So I changed the CropArea custom view a little:
class CropArea(ui.View): def __init__(self): self.hidden = True self.touch_enabled = False self.background_color = (1,0.54,0.24,0.3) self.bounds = (0,0,2,2) def touch_began(self,touch): cx, cy = self.center tx, ty = touch.location self.dxy = tx - cx, ty - cy def touch_moved(self,touch): self.center = self.dxy[0] + touch.location[0], self.dxy[1] + touch.location[1] self.superview.indicator_start.center = self.x, self.y self.superview.indicator_end.center = self.x+self.width,self.y+self.height self.superview.crop_pixels.x = self.x self.superview.crop_pixels.y = self.y + self.height
I also included a
self.crop_area.touch_enabled = True
to thetouch_ended
events of the DragView. As you may guess, it works, however, the crop area flickers between 2 different positions when you move your finger, as shown in this debug I ran:Touch began ('CENTER', 160.25, 258.75) ('TOUCH', 51.0, 60.0) ('DIFF', (-109.25, -198.75)) Touch Moved ('CENTER', (-52.75, -137.75)) ('TOUCH', (269.5, 457.5)) Touch Moved ('CENTER', (167.25, 258.75)) ('TOUCH', (56.5, 61.0)) Touch Moved ('CENTER', (-44.75, -137.25)) ('TOUCH', (276.5, 457.5)) Touch Moved ('CENTER', (176.25, 259.25)) ('TOUCH', (64.5, 61.5)) Touch Moved ('CENTER', (-37.75, -137.25)) ('TOUCH', (285.5, 458.0)) Touch Moved ('CENTER', (181.75, 259.25)) ('TOUCH', (71.5, 61.5)) Touch Moved ('CENTER', (-33.25, -137.25)) ('TOUCH', (291.0, 458.0)) Touch Moved ('CENTER', (184.25, 259.25)) ('TOUCH', (76.0, 61.5))
These prints ran on the CropArea view only, so I guess that when the user moves the finger, it registers 2 touches, 1 based on the DragView and other based on the CropArea. I wonder why they affect the handlers from CropArea.
-
Another debug proves the above, just look for the prev_location:
Touch began ('CENTER', 228.75, 272.0) ('TOUCH', 35.0, 38.0) ('DIFF', (-193.75, -234.0)) Touch Moved ('CENTER', (-155.75, -195.0)) ('TOUCH', (422.5, 506.0)) ('PREV', (419.5, 505.0)) ('ID', 367777440) ('TIME', 207510.7054166667) Touch Moved ('CENTER', (235.25, 272.0)) ('TOUCH', (38.0, 39.0)) ('PREV', (31.5, 39.0)) ('ID', 367777440) ('TIME', 207510.72173520835) Touch Moved ('CENTER', (-147.25, -194.5)) ('TOUCH', (429.0, 506.0)) ('PREV', (420.5, 505.5)) ('ID', 367777440) ('TIME', 207510.73909541668) Touch Moved ('CENTER', (243.75, 273.0)) ('TOUCH', (46.5, 39.5)) ('PREV', (38.0, 38.5)) ('ID', 367777440) ('TIME', 207510.755424125) Touch Moved ('CENTER', (-137.75, -194.0)) ('TOUCH', (437.5, 507.0)) ('PREV', (428.0, 506.5)) ('ID', 367777440) ('TIME', 207510.77168987502)
-
Ok, apparently I remarkably missed to ask the question. But is there a way for these touch_enabled views to work together? CropArea is a subview of DragView. They're both registering touch events and messing things up. If I set
DragView.touch_enabled
to False, then CropArea stops working as well. I tried to set the same frame to both views, so the x and y would remain the same, but I failed miserably and the CropArea still flickers (now, apparently, the DragView event simply sends it to some dark and deep place).I wonder if my only solution would be to create another View to handle editing, it would be a subview of DemoView (therefore, a sibling of DragView, but one layer above) and would affect its drawn elements.
-
Tried that and it still flickers :(
What can I do to help you, Ole?
-
Apparently I worked things out (:
http://www.editorial-workflows.com/workflow/4505745634623488/Jl7G4J6ikPM