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.
Request for an App
-
@JonB If you can find time, could you please take a look at floorplanarea-4? The script works fine but I have found that the slider is very hard to set (it changes the moment I take my finger off it) so I think a better UI would be to have a Scale Setting mode which would permit me to select 2 points for which there is a known distance and to enter the distance in feet and inches. This would also get around the problem I sometimes have where I have had to add a multiplier to the values provided by the slider because I wanted the displayed distance to be larger than the slider allowed.
The basic use case for this script is that I start with a picture of a floorplan which has some lengths shown and I want to determine the area of the space. I added a few lines at the end to permit me to select (using the image picker) an image in the photo gallery. I could not figure out how to feed the picked image to RoomAreaView directly so I used a temp file. I'm sure there must be a better way, but this works. It would also be nice if there were a way to snap the line segments to horizontal and vertical since most (though not all walls are that way). Being able to zoom for small plan images would be nice but I have managed without it.
Thanks very much for your help.
-
I might have forgotton to mention that I added two finger pan/zoom to the version in my repo a while back. Also, old points can be edited by grabbing the handles. This does let you be very precise... I sort of stopped working on this because I realized it probably needs a revamp from the ground up, the way I connected the touch handling with the other ui elements is pretty flawed -- it works, but is not clean, everything depends on everything else.
I will take a look at what you've got.
-
@JonB Wow, I somehow missed that you had done that. I will try it out. With Pan/Zoom done, the most needed feature would be the ability to set scale independent of the area measurement by selecting a known length and entering it as feet/inches.
-
@JonB I tried running RoomAreaFinder.py (using Pythonista 3 with the interpreter set to 2.7) with no success. It opens the console and another tab but nothing else appears.
-
I've installed it and I run it in Pythonista 3 without setting Python 2, but I've had to install swizzle.py, and no problem, all is ok.
Of course you need to install the jpg and Gestures.py -
I will try that although I realize that now that I have switched to Pythonista 3 (v2 crashes on opening) I lost my copy of Stash (which I will use to get swizzle.py). Can you tell me the one liner for getting Stash (I tried import requests as r; exec(r.get('http://j.mp/gsp35').text) but that led to more errors. Is there a different install for Python 2.7 vs 3.5?
-
So, I tried to simply get Swizzle.py by putting it on my Dropbox account and using Dropbox file Picker.py (which worked earlier today) but now I find that when I run the picker, I get a tiny file picker window ??
-
Found I had a Dropbox File Picker.py and a Dropbox picker.py, with the latter working correctly. So now I have the 4 files, RoomAreaFinder.py, swizzle.py, Gestures.py, and the jpg and I still get no output except when I do a zoom gesture and then I get console debug output.
-
@jonb I would really appreciate if you could have a look at the problem I am having running your version with zoom/pan. The console is showing the debug but there is no jpg display. I would love to get this working especially with a scale setting ability as per my earlier comments. This script is really very useful.
-
@ihf I did make a slight update to my repo to make sure I included the right swizzle.py. Try that one.
are you running on ipad or iphone?.
-
I'm using an iPad 3. After deleting old versions of files that may have been causing the problem, your current git is working again. For some reason it seems easier to move points when it is not zoomed but that is a minor issue. Now I just need a way to enter a known distance that may not be part of the area calculation ( e.g. an internal room dimension). What I do now, which is somewhat clumsy, is pick 2 points with a known distance and change the scaling factor from the default of .08 until the distance shown matches the known. Then I have to restart the program (since the points used to determine the scale may not be part of the area), set the scale to the previously determined value and measure the area.
-
@JonB I added the (primitive) file picker snippet to your script to enable selecting an image from the photo gallery. The modified version is in my git as floorplanarea-2.py. I wish I could say I understood your code well enough to make more than trivial changes (e.g. scale setting mode with feet/inches, a way to save the image with the area calculation, etc.) but I sincerely appreciate the work you have already done.
-
Been busy, but I may try to pick this up again soon.
-
@JonB That would be great. In the meantime, I shall try to gain a better understanding of the script.
-
@JonB After not using the script for awhile I recently tried it and it is failing. When I run floorplanarea-6.py https://github.com/ifuchs/RoomAreaFinder/blob/master/floorplanarea-6.py, the file picker comes up, I select an image, the script just ends. If I try to run it again, I get a Value Error, "View is already being presented or animation is in progress".
Also is there a better way to use the selected image than the kluge I added which picks the image and saves to a temp file before calling the main routine (Room AreaView)? -
Strange behavior. I went back to try the app again and it worked ONCE. It has reverted to the behavior described a couple of days ago. It returns immediately after selecting a photo and then if I run it again, it gives a Value error (as if the animation is running already)
-
@JonB Any chance you could take a look at this? Thanks.
-
Is this failing due to a Pythonista bug? It was working on previous releases.
# coding: utf-8 #!python2 # coding: utf-8 import ui import photos class RoomAreaView(ui.View): ''' top level container, consisting of an imageview and an overlay. ideally this might also contain a menu bar, for changing the mode - for instance you might have a button for enabling room drawing, another one for editing points, another for zoom/pan''' def __init__(self, file): # create image view that fills this top level view. # since this RoomAreaView is the one that gets presented, it will be # resized automatically, so we want the imageview to stay tied to the # full view size, so we use flex. Also, the content mode is important # to prevent the image from being squished iv = ui.ImageView(frame=self.bounds) iv.flex = 'wh' iv.content_mode = ui.CONTENT_SCALE_ASPECT_FIT self.add_subview(iv) # right now, read in file from constructor. a future improvement would # be to have a file menu to select an image, which sets the iv.image iv.image = ui.Image.named(file) iv.touch_enabled = False # add the overlay. this is the touch sensitive 'drawing' layer, and # store a refernce to it. this is set to the same size as the # imageview rv = RoomAreaOverlay(frame=self.bounds, flex='wh') self.add_subview(rv) self.rv = rv def will_close(self): '''upon close, dump out the current area. do this by first getting the set of points. The zip notation lets us convert from a tuple of the form ((x0,y0),(x1,y1),...) to x=(x0,x1,...) and y=(y0,y1,...)''' x, y = zip(*self.rv.pts) print polygonArea(x, y, float(self.rv.scale.value)), self.rv.scale.value / 10 class RoomAreaOverlay(ui.View): '''A touch sensitive overlay. Touches add to a list of points, which are then used to compute area. Lengths are shown for each line segment, and a scaling parameter is used to adjust the length of drawn lines to known dimensions''' def __init__(self, *args, **kwargs): # call View.__init__ so we can handle any standard view constructor # arguments, such as frame, bg_color, etc ui.View.__init__(self, *args, **kwargs) self.touch_enabled = True # store list of points, and also current point for use in dragging self.pts = [] self.curridx = -1 # create a textfield to set scale. could be a slider instead # scale is in units of feet/pixel, or really measurementunits/pixel # it might be easier to have user enter this as 1/scale (pixel/measunit), so that it will be a number >1. # If using a Slider instead, use self.scale.value instead of # float(self.scale.text) elsewhere self.scale = ui.Slider(frame=(400, 0, 500, 30)) self.scale.action = self.set_scale self.add_subview(self.scale) # self.scale.text=self.scale.value # create a label showing current computer room area self.lbl = ui.Label(frame=(200, 0, 130, 30)) self.lbl.text = 'Area' self.add_subview(self.lbl) self.lbl.bg_color = 'white' def set_scale(self, sender): '''action for self.scale. called whenever scale is changed. when that happens we update the computation of room area, and also call set_needs_display, which forces a redraw, thus redrawing distance units''' self.update_area() self.set_needs_display() def update_area(self): '''update the area label by computing the polygon area with current scale value''' x, y = zip(*self.pts) area = polygonArea(x, y, self.scale.value / 10) self.lbl.text = 'Area: {} squnits'.format(area) def touch_began(self, touch): '''when starting a touch, fiest check if there are any points very near by. if so, set currpt to that to allow dragging previous points, if not, add a new point ''' self.curridx = -1 currpt = (touch.location.x, touch.location.y) # search existing points for a near match for i, p in enumerate(self.pts): if abs(ui.Point(*p) - ui.Point(*currpt)) < 20: self.curridx = i # if not match found, add a new point if self.curridx == -1: self.pts.append(currpt) self.set_needs_display() def touch_moved(self, touch): ''' update the current point, and redraw''' self.pts[self.curridx] = (touch.location.x, touch.location.y) self.set_needs_display() def touch_ended(self, touch): ''' called when lifting finger. append the final point to the permanent list of pts, then clear the current point, and redraw''' self.curridx = -1 self.set_needs_display() self.update_area() def draw(self): ''' called by iOS whenever set_needs_display is called, or whenever os decides it is needed, for instance view rotates''' # set color to red ui.set_color((1, 0, 0)) # create a copy of self.pts, and append the current point drawpts = self.pts if drawpts: # move path to first point: pth = ui.Path() pth.move_to(*drawpts[0]) p0 = drawpts[0] for p in drawpts[1:]: # for each point after the first, draw a line from the previous point, compute length vector L # draw the line segment pth.line_to(*p) # compute point which is halfway along line between the # start/end point. L is the vector pointing from start to # finish. H is the Point at the halfway, referenced back to # global origin L = (ui.Point(*p) - ui.Point(*p0)) P0 = ui.Point(*p0) H = P0 + L / 2 # halfway point # put text at the halfway point, containing length ofmsegment * # scale ui.draw_string('%3.2f' % abs( L * self.scale.value / 10), (H.x, H.y, 0, 0), color='red') # set starting point for next line segment p0 = p pth.stroke() # draw the path if len(drawpts) > 2: # 'close' the path to show area computed with ui.GState(): pth = ui.Path() pth.move_to(*drawpts[-1]) pth.line_to(*drawpts[0]) pth.set_line_dash([2, 3]) ui.set_color((1, .5, .5, .5)) pth.stroke() # create a 10 pixel circle at last entered point ui.Path.oval(drawpts[-1][0] - 5, drawpts[-1][1] - 5, 10, 10).fill() # show circles for previously entered points. smaller and lighter ui.set_color((1, .5, .5)) for p in drawpts[0:-1]: ui.Path.oval(p[0] - 3, p[1] - 3, 6, 6).fill() def polygonArea(X, Y, scale): '''compute scaled area of polygon,assuming it is closed ''' area = 0 j = len(X) - 1 for i in range(len(X)): area = area + (X[j] + X[i]) * (Y[j] - Y[i]) * scale**2 j = i return abs(area / 2) photos.pick_image().save("temp.jpg") v = RoomAreaView('temp.jpg') v.present('fullscreen')```
-
What is the error you are getting? PAste the full traceback.
I am getting a IOError: cannot write mode P as JPEG, which im looking at... -
Try this:
p=photos.pick_image() p.save("temp.jpg") #we need to allow some time for animation to completely complete import time time.sleep(1) v = RoomAreaView('temp.jpg') v.present()
I cant really explain why, but it seems that trying to present a view immediately after pick_image() doesnt quite allow the animation to complete, at least sometimes. Adding a little time seems to work. 1 second is probably overkill,myou can experiment with it.