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
-
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?
-
@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)))
-
@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.
-
@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
-
@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.
-
@mikael Sorry, I didn't understand correctly your request (it happens frequently for me 😢)
and whaaaa for caretRectForPosition, never seen it -
-
@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
-
@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)
-
@mikael you can use rect.size to avoid measure_string
-
@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 😀
-
@cvp, rect.size is in this case just the size of the caret, i.e. roughly right height but very thin.
-
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.?
-
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
-
@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 atinsert_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()
-
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