omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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

    Pythonista
    13
    93
    49615
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ihf
      ihf last edited by ihf

      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.

      1 Reply Last reply Reply Quote 0
      • ihf
        ihf last edited by

        @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.

        1 Reply Last reply Reply Quote 1
        • JonB
          JonB last edited by

          Been busy, but I may try to pick this up again soon.

          1 Reply Last reply Reply Quote 0
          • ihf
            ihf last edited by

            @JonB That would be great. In the meantime, I shall try to gain a better understanding of the script.

            1 Reply Last reply Reply Quote 1
            • ihf
              ihf last edited by

              @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)?

              1 Reply Last reply Reply Quote 0
              • ihf
                ihf last edited by ihf

                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)

                1 Reply Last reply Reply Quote 0
                • ihf
                  ihf last edited by

                  @JonB Any chance you could take a look at this? Thanks.

                  1 Reply Last reply Reply Quote 0
                  • ihf
                    ihf last edited by

                    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')```
                    1 Reply Last reply Reply Quote 0
                    • JonB
                      JonB last edited by

                      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...

                      1 Reply Last reply Reply Quote 0
                      • JonB
                        JonB last edited by

                        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.

                        1 Reply Last reply Reply Quote 0
                        • ihf
                          ihf last edited by

                          The first time I execute this script after Pythonista is started, the file picker comes up, I select an image, the script ends ( i.e. The run triangle returns) and no window is opened. If I run it again, I get:

                          Traceback (most recent call last):
                          File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/floorplanarea-6.py", line 160, in <module>
                          v.present('fullscreen')
                          ValueError: View is already being presented or animation is in progress

                          At this point Pythonista needs to be quit or any app that tries to present a view will fail similarly.

                          1 Reply Last reply Reply Quote 0
                          • JonB
                            JonB last edited by

                            Did you try my delay approach? You will need to force quit pythonista, then make the above mod.

                            1 Reply Last reply Reply Quote 0
                            • ihf
                              ihf last edited by

                              Just tried it and it worked! Is there a better way to do the image selecting that does not involve saving the temp file at all?

                              Unrelated question: this script is extremely useful and would be much easier to use if I could select 2 points that define one known dimension and set the scale by entering it as feet and inches rather than using a slider. What I do now is select 2 points and move the slider until it is as close as I can get to the printed dimension but I can never get it quite right as the slider always changes as I take my finger off and the delta for a small movement is enough to make the positioning tricky.
                              Thanks so much for your expert help.

                              1 Reply Last reply Reply Quote 0
                              • JonB
                                JonB last edited by

                                I am (slowly) working on an update that will have a much nicer user interface, allow pinch zooming for precision point placement, and have support for multiple rooms, saving and reloading results, and presenting summaries etc.

                                But things go slowly...

                                1 Reply Last reply Reply Quote 0
                                • ihf
                                  ihf last edited by

                                  That's great! I understand and I shall be patient. Would it help in designing the UI if I posted links to 1 or 2 typical floor plans to give a better idea of what I'm trying to do?

                                  1 Reply Last reply Reply Quote 0
                                  • ihf
                                    ihf last edited by

                                    @jonb I would be happy to discuss use cases here or, if you prefer, outside the forum. One thing you did not mention is the ability to set scale, start entering points and then clear points/lines without reentering scale. Also,, while it does not affect the area calculation very much, it would be nice to have the option to force a line segment to be vertical or horizontal as most floorplans are oriented in this way and positioning the line is a bit tricky. Having the ability to store the plan with the scale and area would be VERY helpful.I am convinced that with the additions you mention, this could be successfully sold on the App Store.

                                    1 Reply Last reply Reply Quote 0
                                    • JonB
                                      JonB last edited by

                                      I pushed a change which includes the ability to two finger pinch zoom, and two finger pan.
                                      I made this change a long time ago, but i now added in your file picker. Points can be moved by dragging on an existing point, thus you can set your scale, and then move points, or if you dont get it quite exact, you can zoom in and tweak.

                                      I updated the scale textbox to allow simple math. For instance, if scal is currently 0.08, you draw a line which says 23.2, but the real distance is 25 you could enter 0.08*25/23.2, and the scale will automatically be set so your length is exact.

                                      I also added a clear button to clear the path.

                                      https://github.com/jsbain/RoomAreaFinder.git

                                      anyway, just sort of temporary changes using the old ui.

                                      1 Reply Last reply Reply Quote 0
                                      • ihf
                                        ihf last edited by

                                        @jonb Here is a "real world" image of a floor plan that I am trying to measure. If I zoom in, the red circle is sometimes too large to position accurately. Also, would be nice to be able to measure the total area as well as the area of internal rooms or spaces. Right now I can do either but not both on one plan. Also, might be better if the window were full screen?

                                        1 Reply Last reply Reply Quote 0
                                        • L
                                          Lesternixon @TutorialDoctor last edited by

                                          The quickest method would probably be to import the jpg and trace it using the pen tool. Simply add a small amount of code to the traced object to compute the area dynamically. Although your situation is highly particular, I believe it will serve your needs. I'm currently working on that screenshot.

                                          ihf 1 Reply Last reply Reply Quote 0
                                          • ihf
                                            ihf @Lesternixon last edited by

                                            @Lesternixon Thanks. @jonb wrote a fantastic script that does exactly what I wanted. Unfortunately with the new release of Pythonista it fails due to a problem with the image module. Hopefully this will get fixed at some point.

                                            cvp 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors