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.


    Locating the exact position of a piece of text

    Pythonista
    3
    16
    7990
    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.
    • mikael
      mikael last edited by

      I would like to place some active elements (like a checkbox) on top of a TextView, based on where some specific piece of text is located. In a WebView, I could wrap the text in a span element and locate that, but I do not want to use a WebView here.

      I tried to use ui.measure_string, but I am not getting exact enough results even for the y coordinate, and do not even have an idea how I could use it for the x.

      Would anyone have any ideas how to accomplish this?

      cvp 1 Reply Last reply Reply Quote 0
      • cvp
        cvp @mikael last edited by

        @mikael try this one

        import ImageFont
        import ui
        text = 'test'
        font = ImageFont.truetype('Courier',20)
        print(font.getsize(text))
        print(ui.measure_string(text,font=('Courier',20)))
        
        mikael 1 Reply Last reply Reply Quote 1
        • mikael
          mikael @cvp last edited by

          @cvp, thanks. I was not familiar with ImageFont.

          The two do return significantly different values in your example:

          • ImageFont: (64, 17)
          • measure_string: (48.01, 20.00)

          ... and thus the first might be more accurate. But I do not see any way in ImageFont to restrict the horizontal size to get the right height for multiline text.

          cvp 1 Reply Last reply Reply Quote 0
          • cvp
            cvp @mikael last edited by

            @mikael this?

            import ImageFont
            text = 'test'
            max_width = 35
            font_size = 20
            while True:
            	font = ImageFont.truetype('Courier',font_size)
            	w,h = font.getsize(text)
            	print(font_size,w,h)
            	if w <= max_width:
            		break
            	font_size = font_size - 1
            
            mikael 1 Reply Last reply Reply Quote 0
            • mikael
              mikael @cvp last edited by mikael

              @cvp, looks like you are making the font size smaller until the text fits in the max_width. The issue here was that with a longer piece of text, TextView will split it into multiple lines using ”soft” line breaks, which will move the location of the target piece of text down in the view. ImageFont is not useful here since it only understands measuring the text as a single line plus any ”hard” line breaks.

              In other views, I found caretRectForPosition for UITextView, which will probably be the solution for my issue.

              cvp 3 Replies Last reply Reply Quote 0
              • cvp
                cvp @mikael last edited by cvp

                @mikael Sorry, I didn't understand correctly your request (it happens frequently for me 😢)
                and whaaaa for caretRectForPosition, never seen it

                1 Reply Last reply Reply Quote 0
                • cvp
                  cvp @mikael last edited by

                  @mikael something like

                  1 Reply Last reply Reply Quote 0
                  • cvp
                    cvp @mikael last edited by cvp

                    @mikael Please, try this

                    import ui
                    from objc_util import *
                    tv = ui.TextView()
                    tv.font = ('<System-Bold>',32)
                    tv.text = 'thid is a sample but could be longer'
                    tv.frame = (0,0,200,200)
                    tv.present('sheet')
                    tvo = ObjCInstance(tv)
                    #print(dir(tvo))
                    txt = 'coul'
                    i = tv.text.find(txt)
                    p1 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i)
                    p2 = tvo.positionFromPosition_offset_(tvo.beginningOfDocument(), i+len(txt))
                    rge = tvo.textRangeFromPosition_toPosition_(p1,p2)
                    rect = tvo.firstRectForRange_(rge)	# CGRect
                    x,y = rect.origin.x,rect.origin.y
                    w,h = rect.size.width,rect.size.height
                    print(x,y,w,h)
                    l = ui.Label()
                    l.frame = (x,y,w,h)
                    l.background_color = (1,0,0,0.5)
                    tv.add_subview(l)
                    

                    Or use a button instead a label

                    tv.text = 'this is a sample but could be longer'
                    ...
                    txt = 'ampl'
                    ...
                    l = ui.Button()
                    l.frame = (x,y,w,h)
                    l.background_color = (1,0,0,0.5)
                    l.corner_radius = 10
                    l.border_width = 1
                    def button_action(sender):
                    	if l.background_color == (1,0,0,0.5):
                    		sender.background_color = (0,0,1,0.5)
                    	else:
                    		sender.background_color = (1,0,0,0.5)
                    l.action = button_action
                    

                    mikael 1 Reply Last reply Reply Quote 0
                    • mikael
                      mikael @cvp last edited by

                      @cvp, heh, parallel evolution. Here’s mine, providing an exact match:

                      #coding: utf-8
                      from ui import *
                      
                      f = ('<system>', 14)
                      v = TextView(font=f)
                      v.text = 'Some text including the text to find the position of'
                      v.present()
                      
                      vo = v.objc_instance
                      caret_pos = vo.selectedTextRange().start()
                      
                      s = 'text to find'
                      pos = v.text.find(s)
                      caret_pos.setOffset_(pos)
                      rect = vo.caretRectForPosition_(caret_pos)
                      (x,y) = rect.origin.x, rect.origin.y
                      (w,h) = measure_string(s, font=f)
                      
                      l = View()
                      l.frame = (x,y,w,h)
                      l.background_color = (1,0,0,0.5)
                      v.add_subview(l)
                      
                      cvp 2 Replies Last reply Reply Quote 0
                      • cvp
                        cvp @mikael last edited by

                        @mikael you can use rect.size to avoid measure_string

                        mikael 1 Reply Last reply Reply Quote 0
                        • cvp
                          cvp @mikael last edited by

                          @mikael and if the searched text is splitted on several lines, a more complex code has to be written, but anyway, I'm happy to have learned something new, as usual 😀

                          1 Reply Last reply Reply Quote 0
                          • mikael
                            mikael @cvp last edited by

                            @cvp, rect.size is in this case just the size of the caret, i.e. roughly right height but very thin.

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

                              This came out pretty rad, if I may use the term.

                              Editing in Markdown, I can just type [x], and a checkbox pops out to hover over it. Tapping the checkbox changes the text underneath to [ ] and back. I can select and delete the checkbox because it is actually the underlying text that gets deleted.

                              Other ideas for ”active text” that would go beyond Apple’s data recognizers for phone numbers etc.?

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

                                mikael, i'm trying to follow what you're trying to do here. if i wanted to select text in a markdown document, hover over the selection and then use it to "wrap" (ie insert double colons on either end of the text selection which is somewhat similar perhaps to you inserting and maybe toggling a checkbox i think???) the text, would your code be beneficial. i'm not sure i understand what it does ;) thx

                                basically i love editorial but there are a few customizations i would like to make that aren't possible so i'm wondering if i might be able to simply "mark" my md documents by baking my own simple md editor in pythonista that only provides the basic extra but stripped-down functionality i need. in sum, i like to highlight specific words and phrases in documents for learning purposes. highlight means toggle on / off wrapping in pairs of my own custom tags. i would also be able to change the default background color for the wrapped pair text.

                                i've never done any gui programming before. just trying to get some insights where to start. thx

                                mikael 1 Reply Last reply Reply Quote 0
                                • mikael
                                  mikael @robopu last edited by

                                  @robopu, no, it sounds to me like this topic is not what you are after although you will need to use objc_util for that as well.

                                  For adding characters around highlighted text, I would recommend using additional keys above the keyboard. As a sample, check MarkdownView. There, look at create_accessory_toolbar method. As a sample of adding specific characters around the selected text, look at insert_character method.

                                  Changing the background of selected text uses "attributed strings" - there are threads on that, but below is an example for the background color. Check this thread for other formatting options.

                                  import ui
                                  from objc_util import *
                                  
                                  @on_main_thread
                                  def change_background():
                                    red = UIColor.yellowColor()
                                    mystring = ObjCClass('NSMutableAttributedString').alloc() 
                                    teststring = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec tincidunt facilisis dapibus.'
                                    mystring.initWithString_(teststring)
                                    mystring.setAttributes_range_({'NSBackgroundColor': red}, NSRange(28, 11)) # start, length
                                    tv = ui.TextView()
                                    tvc = ObjCInstance(tv)
                                    tvc.attributedText = mystring 
                                    tv.present()
                                    
                                  change_background()
                                  
                                  1 Reply Last reply Reply Quote 0
                                  • robopu
                                    robopu last edited by robopu

                                    hey thanks @mikael. markdownview looks like it has the kind of functionality i'm looking for. i'll have to explore that. i'm coming over from editorial and haven't quite figured out yet what the best way is to download these scripts into pythonista on my iphone. once i do, i'll check this out. thx for the help

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