Forum Archive

Find text in console

Matteo

Hello, do you know a way (I searched in the forum but not found answer) to find text (also with regex) in console output field, select it, copy it in clipboard after the execution of a script or a command?

Simple example: I write in the console command line print("hello dear") and press run: Pythonista executes the command and prints hello dear in the console output window. Well, I'd like, when I'm in console environment and as post-processing, to find only word dear (with direct searching of word dear or with regex, with pattern that selects all after word hello), select and copy it, or simply select it with a colored highlighting (in the same way user can do it in editor environment of Pythonista, where Pythonista highlights the searched text).

So, can Pythonista allow user to find text in console output? If not, do you know some existing obj-c script written by someone to create a wrench button to search and highlight text in console (maybe with pop-up in order to insert the word to be searched)?

Thank you
Regards

cvp

@Matteo do you remember your topic.
I think it should be easy to write a tool because you can get the console text.

Matteo

@cvp Hi, yes remember, I use your neat code to send to server some short commands that I write in console to obtain answer to be printed in console output, but I think it cant help me.

I explain better: I don't need to copy in clipboard the text that I write in console input line, but I need to search/find/highligth some text in console output window (lets call it: the console history). For example, suppose I have a lot of text in console output, because I executed a lot of scripts that print a lot of things in console output: how can I find some specific text by using the built-in function Find (like in editor environment)?

Thank you
Bye

cvp

@Matteo I've understood your request. It was only to remember that if we can access the console content, we should be able to scan it to find your words, but not sure we could change the attributes (color) because the console is not a real TextView but an OMTextView which does not have the needed methods to change attributes.

Edit: this find is easy but to show where they are is not

Matteo

@cvp Ok, thank you for your reply, so I understand that console has not a Find field like the editor and by using your code user can keep track of what has been written in console, but the problem could be how to keep track of what has been written directly from script execution (so from editor, not from console).
However do you know some obj-c method to interact with Pythonista console (in this specific case with history of the console, not with input editable line of console environment)?

Thanks

cvp

@Matteo before iOS 13 and/or beta version, my little GetConsoleText script did get the console content, thus the historic.But, if I run it now, this is empty, thus my script does not work anymore. I (would) have to understand why 😢

cvp

@Matteo I think I'm wrong, very sorry. I never got full console history, at least it seems.
I'll search for you, but not free immediately

Matteo

Hi @cvp , thank you so much for your time but don't worry, don't search for me, only if it is also your interest, no problem, really :-).
My interest is if a code already exists that can search text in console, when there is a lot text in it and user doesn't want to search some specific words by scolling down the screen.

I will try to search in official Apple doc about some obj-c for interaction with OMTextView. Maybe I will find something here (found now).

Thank you for your always lively kindness in answering in this forum!
Regards

cvp

@Matteo I think that OM... objects are from OMz, thus not in Apple doc

Matteo

@cvp Hi, do you mean OMxxxxx is something related to omz library, not Apple?

However, if it is programmatically impossible to interact with console output via objc, do you know something for pythonista_startup.py that can automatically save in a file (named for example console_output.txt) the output of any command executed in console or any script executed from editor environment, in a consecutive mode, in the same way the output in console appears? This file should contain the exact text user sees in console output/history, and this file should update itself after each execution where there is at least one print statement (that is when the console output changes its content by adding new text output).

Do you know if something already exists?

Thank you
Bye

cvp

@Matteo Please could you try this code as a Pythonista tool.
First, you run the tool,
then you run a script with a console output,
then, in console mode, you type the text fo search and tap the 🔍 icon, and you will watch the miracle 😀

Sure that the code is not bug free, but it is good to start, if interested

The OMTextView does not allow to set text attributes as an UITextView but you can draw on it

from objc_util import *
import clipboard
import ui
@on_main_thread
def test(sender):
    import console
    import re
    import ui
    txt = str(sender.console.text())
    if txt[-1] == '\n':
        txt = txt[:-1]
    win = ObjCClass('UIApplication').sharedApplication().keyWindow()
    main_view = win.rootViewController().view() 
    ret = ''
    def analyze(v):
        for tv in v.subviews():
            if 'OMTextView' in str(tv._get_objc_classname()):
                su = tv.superview()
                if 'OMTextEditorView' in str(su._get_objc_classname()): 
                    continue    
                for sv in tv.subviews():
                    if 'SUIButton_PY3' in str(sv._get_objc_classname()):
                        sv.removeFromSuperview()
                if txt == '':
                    return
                t = str(tv.text())
                #print('search',txt,'in',t)
                for m in re.finditer(txt, t):
                    st,end=m.span()
                    p1 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), st)
                    p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), st+len(txt))
                    rge = tv.textRangeFromPosition_toPosition_(p1,p2)
                    rect = tv.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.Button()
                    l.frame = (x,y,w,h)
                    l.background_color = (1,0,0,0.2)
                    l.corner_radius = 4
                    l.border_width = 1
                    tv.addSubview_(l)
            ret = analyze(tv)
            if ret:
                return ret
    ret = analyze(main_view)

@on_main_thread
def FindTextInConsole():
    global console_tv
    win = ObjCClass('UIApplication').sharedApplication().keyWindow()
    main_view = win.rootViewController().view() 
    ret = '' 
    next_is_console = False
    def analyze(v,indent):
        global next_is_console
        ret = None
        for sv in v.subviews():
            #print(indent,sv._get_objc_classname())
            if 'UILabel' in str(sv._get_objc_classname()):
                #print(indent,sv.text())
                if str(sv.text()) == '>':
                    next_is_console = sv
                else:
                    next_is_console = False
            elif 'OMTextView' in str(sv._get_objc_classname()): 
                if next_is_console:
                    su = next_is_console.superview()
                    for ssv in su.subviews():
                        if 'SUIButton_PY3'in str(ssv._get_objc_classname()):
                            # rerun of this script, remove previous button
                            ssv.removeFromSuperview()
                    b = ui.Button(name='clipboard')
                    b.tint_color ='red'
                    b.image = ui.Image.named('iob:ios7_search_32')
                    b.background_color = 'white'
                    h = su.frame().size.height
                    b.frame = (2,2,h-4,h-4)
                    b.action = test
                    b.console = sv
                    #print(dir(sv))
                    retain_global(b)
                    su.addSubview(ObjCInstance(b))

            ret = analyze(sv,indent+'  ')
            if ret:
                return ret
    ret = analyze(main_view,'')
    return ret

if __name__ == '__main__':  
    r = FindTextInConsole()

cvp

@Matteo Sorry, I did not see your last post, just before my script.
If you check this one, you will see the line

                t = str(tv.text()) 

it contains the full console...

cvp

@Matteo said:

do you mean OMxxxxx is something related to omz library, not Apple?

Yes

cvp

Code edited, crashed if text to search empty (to clean console).
I said there were bugs, isn't it? 😀
Added

                if txt == '':
                    return 
mikael

@cvp, @Matteo, very useful, this should be a standard feature of Pythonista.

pavlinb

Just brilliant.

Matteo

@cvp Hi, I haven't tried it yet your code but it is impressive! Thank you :-)

I sometimes feel that there is a possibility to do something with programming, but every time I see that one of you programmers really manages to do it, I am fascinated.

I want to try your code by saving it in the StatusBarMenu (by JonB) as an action to run with the little buttons of it. So my dream is to associate your script to a user key of StatusBarMenu in order to enable/disable the search text function.
So do you know if it is possible to disable your search function (with a click) if user wants to return to use the console to run python commands instead of to search text?

Thank you very much
Regards

cvp

@Matteo It is not needed. Even if you activate the search, the normal use of the console command still works by pressing enter. The search is only done if you tap the 🔍 icon.

Matteo

@mikael Hi, I still use Pythonista version 3.1 (I have an old iphone with ios 10.3.3) and it is a great software, I find it very exciting to think that user can even customize the interface of this application as you wish, if knows how to do it ;-)

Pythonista is Great!

cvp

@Matteo said:

Pythonista is Great!

Full agree. I hope you have seen my previous post because we wrote at the same time

Matteo

@cvp Yes! you are right, sorry, two buttons on console line! You created a second button at left of input line, Thank you again!

cvp

@Matteo said:

two buttons on console line

Do you see two buttons on the console line?

Matteo

@cvp no no, I badly explained myself sorry, I meant I see your button search at left, at right I see the built-in recall command icons and the execute command is the return key of keyboard, yes sorry. I'm excited...

cvp

@Matteo 👍

Matteo

Hi @cvp, thanks again for your code, it helps me a lot! I tried to change it in order to clear highlighting of the text before each touch of the "Find" button, because if I search different words, the console becomes too colored and I can't understand where the last word is selected.

If you want and have time, can you show me how to change your code in order to remove all highlighting before each touch of your "Find" user key?

Thanks
Bye

cvp

@Matteo that is strange, because when you tap the find button, I remove all colored buttons before to create new ones, thus no overlap of multiple searches.
Did you change something?
I just retry and there is no problem...

Could you post your code if not exactly mine

Matteo

@cvp Hi thanks for reply, with Pythonista I use (v.3.1 301016) on ios 10.3.3 I have different behavior from you , that is when I touch Find key , the previous highligtings can't disappear (text become red and more red...). Maybe it is due to different objc implementation of new version of Pythonista.

I will try again , if I will notice some different behavior I will let you know.

Thank you
Regards

cvp

@Matteo ok. I use Pythonista last beta on iOS 13.3.

Matteo

@cvp Hi, sorry I'm feel very unskilled, no objc problem, I must use Python3, no Python2...

Problem solved!

Sorry I ask you :when you touch built-in key Clear in console do you have the clear of full console content and the color red of last highligthed text?

Thanks

cvp

@Matteo I thought about this problem of python 2 or 3 overnight and I wanted to check this morning because I "remove SUIButton_PY3 objects" ... Great that you found it

cvp

@Matteo said:

clear of full console content and the color red of last highligthed text?

You're right, that's a problem. My script does not know when you tap the clear button thus it is not able to also clear the couloured buttons. I'll try to find something but not promised

temporarily, you would have to clear manually the console line then tap the 🔍 button, colorful buttons will be removed

Edit: if you have cleared the console, tap only the 🔍 button, even without clearing the search text, all highlighted words will disappear

Matteo

@cvp You are right, to clear all I can use your Search button with empty text, perfect.

Many thanks for your help!

Regards

cvp

@Matteo And, if you tap the standard clear button, you can, after, tap the search button even without clearing the searched text. As the console is cleared, there is no occurrence to find, thus all highlighted will be removed

cvp

@Matteo small update if you want a case insensitive search

                for m in re.finditer('(?i)'+txt, t): 
Matteo

Hi @cvp , nice, yes no need to clear the search text field! And thanks also for insensitive search!

Sorry if I ask you something else, answer only if it's something that interests you too: is it possible to have a second version of your script that can search text with python Regex? I think a second script for only Regex would be simpler than adding a setting flag (that user could change by executing a script associated to another custom user key) that allows user to switch from regex to simple text and viceversa with your original script.

For example, if I have the console output with a lot of text data and I want to select all text (numbers) after word value: , I'd like to use the regex pattern (?<=value: )(.*) in order to highlight all after word value: (to the new row/line).

Do you think it is possible? I ask you this because, although it is something that would help me a lot, I can't understand how to enable Regex searching by taking inspiration from your update about insensitive search.

Thanks
Bye

cvp

@Matteo these lines can find both texts or regex patterns:

                for m in re.finditer(txt, t):
                    st,en =m.span()
                    p1 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), st)
                    p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), en) 

Please, note that if you type a text, it is case sensitive (back)

And you don't need two different search buttons

Note also that the regex you gave as example needs a space after value:

Matteo

@cvp Wonderful!

I can't understand how it is possible to have regex mode by modifying the code from

st,end=m.span()
...
p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), st+len(txt))

to

st,en =m.span()
...
p2 = tv.positionFromPosition_offset_(tv.beginningOfDocument(), en)

Magic of programming and of those who know how to use this magic ...

Thank you very much!
Regards

cvp

@Matteo no real magic. 1st version was ok but only for a text. 2nd version really uses start and end of search

cvp

@Matteo if you want to use different colors to search different words, you can use the regex | and modify the script as


                    if '|' not in txt:
                        l.background_color = (1,0,0,0.2)
                    else:
                        # search multiple strings
                        wrds = txt.split('|')
                        idx = wrds.index(t[st:en])
                        cols = [(1,0,0,0.2), (0,1,0,0.2), (0,0,1,0.2), (1,1,0,0.2), (1,0,1,0.2), (0,1,1,0.2)]
                        col = cols[idx % len(cols)]
                        l.background_color = col

Obviously, you can put the cols initialization outside the loop but, here, it is only to show only one group of modified lines

Matteo

@cvp Thank you, very nice and useful!

As a curiosity, maybe I didn't understand before: do you know if the highlighted text with your script can be also copied in clipboard or it is impossible due to limits of OMTextView?

If it is impossible to copy the highlighted text in Pythonista clipboard, do you know if a script exists that can write (in background, that is during Pythonista usage), in an external text file, the full output of the console, and the external file updates itself after each output print in console?

Thank you for your interest :-)
Bye

cvp

@Matteo said:

do you know if the highlighted text with your script can be also copied in clipboard

Sure, it can. Do you want I modify the script to copy all highlighted texts into the clipboard?
If yes, how to separate all highlighted texts?

cvp

@Matteo try

                clp = ''
                for m in re.finditer(txt, t):
                    .
                    .
                    .
                    tv.addSubview_(l)
                    clp += t[st:en] + '\n'
                clipboard.set(clp)

Perform your search, then paste the clipboard to check its content
Here, each highlighted text will be a new line, as example

Matteo

Hi @cvp, exactly, you read me in thought!

The choice to copy the highlighted text so that each word is on a different line is perfect for me, in this way after some calculation I can screenshot iphone screen with some highlighted text (also with different colors, thank you) and to send image to someone (it is easier to explain something with some text highlighted), with also the full string matched by your script as a text file, for further analysis (with spreadsheets or other tools).

Thank you !
Regards

cvp

@Matteo said:

, you read me in thought!

Nothing so magic, I only have hacked your front camera 😂

Matteo

@cvp :-) , but my phone is so old and with storage memory so clogged that the hack works only randomly...;-)

cvp

@Matteo not sure that comes from the iPhone age, but perhaps due to mine 😀
I also got some problems, try the imports also at def module, because the script could be no more active but the def of the search button yes

from objc_util import *
import ui

@on_main_thread
def test(sender):
    import clipboard
    import console
    from objc_util import ObjCClass
    import re
    import ui