How to prevent commands execution in Pythonista console
Hi, do you know a way (with some esoteric objc maybe?) to bypass (prevent the execution of) the Pythonista internal python interpreter when user presses the key return in built-in keyboard in the Python console after writing a command, in order to run other external scripts written by user?
For example: when I'm in the Pythonista console and I write:
>>> print('hello') ## and press "return" key in the keyboard
I'd like to force Pythonista to execute a script X instead of the normal execution (that is the execution of the command "print('hello')" by the internal python interpreter of Pythonista). So, following this example, I don't want the common output that is:
but I'd like to have, as output and as a simple example, the number of single characters in the string of the command "print('hello')" (if the script X is a script that reads the string written by user and counts its characters). So when I press return key, I'd like to see in console the output:
that are the number of characters of the input string "print('hello')".
The script X that counts the characters of the user input string in console is only an example, only to explain better my question.
Hi, I’ve found something related to my question in this forum and I’ve tried to search in the web something about objc, but without success.
We know that the following code (found thanks to this forum) works for editor environment (as a common wrench action or user key defined with StatusBarMenu by JonB):
import editor, clipboard i=editor.get_line_selection() t=editor.get_text() clipboard.set(t[i:i-len(t)+len(editor.get_text())])
So I can, with one single click (two clicks if built-in wrench action is used), to copy in clipboard the entire line within the active cursor. How can I do the same in console environment?
I mean: in console environment I’m interested in searching a way (sequence of objc commands) to simulate in one single action the following actions:
- the touch with finger on the active text field in console (where user inserts the command to be launched);
- the selection of all text when pop-up is shown (with pop-up key
- the copy of the text into clipboard (with pop-up key
Copy) in order to post-process the string.
Thanks for any help about programmatic use of the built-in keys in the Pythonista pop-up (see here) when user touches screen on text in editor and console (probably through the use of objc).
@Matteo I think this code, as a tool, does what you want, try and tell me
from objc_util import * import clipboard def GetConsoleText(): 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()): if str(sv.text()) == '>': next_is_console = True else: next_is_console = False elif 'OMTextView' in str(sv._get_objc_classname()): #print(sv.text(),next_is_console) if next_is_console: return str(sv.text()) ret = analyze(sv,indent+' ') if ret: return ret ret = analyze(main_view,'') return ret r = GetConsoleText() clipboard.set(r)
@Matteo Still a better script: you run it once as a tool or not and it installs a "copy" button at left of the console
from objc_util import * import clipboard import ui def GetConsoleText(): 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()): #print(sv.text(),next_is_console) 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_copy_outline_32') b.background_color = 'white' h = su.frame().size.height b.frame = (2,2,h-4,h-4) def test(sender): t = str(sender.console.text()) if t[-1] == '\n': t = t[:-1] clipboard.set(t) b.action = test b.console = sv retain_global(b) su.addSubview(ObjCInstance(b)) return str(sv.text()) ret = analyze(sv,indent+' ') if ret: return ret ret = analyze(main_view,'') return ret if __name__ == '__main__': r = GetConsoleText()
Hi @cvp, WOW, I am always fascinated when some people (like you) can write certain codes, here I feel like a child in a toy store! This forum is really full of creative people!
Sorry for delay of my response, your first script works very well, I was trying to understand it (without much success), I added it in @JonB StatusBarMenu as an action (I use this version, slightly modified, on my old little iPhone 5s with Pythonista version 3.1 - 301016, yes I still use that version ;-) ), but it is really too advanced for me, it is enough for me it works very well.
Then I tried your second script and I didn't think such that thing was possible: that is the customization of the input field in console environment!
Now (after some dive in objc) it is possible to process with one single touch each console input.
My next step is to understand how to recover functions, definitions and global variables like numbers, strings, arrays, etc... created in built-in Pythonista interpreter in order to pass them in the string in console as arguments of any function that must be executed by a remote interpreter (for example when the function is not available in Pythonista, I use a lot Scipy for example).
Many thanks for your help!
@Matteo Happy to help.
My last code has perhaps some lines too much because I did not remove (old) part for copying the console line in the clipboard without tapping the icon.
The action is actually named "test" but you can of course write there your own code
def test(sender): t = str(sender.console.text()) if t[-1] == '\n': t = t[:-1] sender.console.setText_('console_input="'+t+'"') clipboard.set(t)
You type a=1.5, then tap the "copy" icon, and you get
And you still have to type the return key...
Thank you @cvp , I think
GetConsoleText(), based on your previous post. Am I right? And I can't understand
console.setText_: what is it?
@Matteo no, no.
When I create the button b, I save as an attribute b.console, the associated textview sv.
When the button is pressed, its action is executed.
Sender is the button, thus sender.console is the associated textview.
sender.console.text is thus textview.text
We change the text and setText_() is a method of the textview to change its content
That's all folks 😀
Edit: this code is IN GetConsoleText thus I can't call it-self
@cvp thank you for explanation, unfortunately I have not familiarity with words like "attribute" and "method" (object-oriented programming). Anyway your code works so it is enough for me :-)