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.
ADD shortcut to Home Screen
-
Based on @omz script Add to Home Screen,
this script allows the creation of an home screen shortcut to run a script- to be selected in Pythonista local or iCloud files
- to define any icon web file, not only builtin icons
- to add eventual arguments to the url scheme
The safari page to share to an home screen shortcut shows the icon, its url, its title and the url scheme.
These informations need to be entered in a form_dialog:
- when you put the cursor on the script field, you get a file picker
- when you modify the icon url, the image is directly showed
# from http://www.editorial-workflows.com/workflow/5872060161064960/e831ijSVsok # converted for python3 which uses bytes ipo strings import dialogs import http.server import webbrowser import base64 import os import ui import dialogs import requests from File_Picker import * page_template = '''<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <meta charset="utf-8"> <title> {{TITLE}} </title> <link rel="apple-touch-icon" sizes="76x76" href="{{ICON_URL}}"> <link rel="apple-touch-icon" sizes="120x120" href="{{ICON_URL}}"> <link rel="apple-touch-icon" sizes="152x152" href="{{ICON_URL}}"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="viewport" content="initial-scale=1 maximum-scale=1 user-scalable=no"> <style type="text/css"> body { background-color: #023a4e; -webkit-text-size-adjust: 100%; -webkit-user-select: none; } #help { display: none; color: white; font-family: "Avenir Next", helvetica, sans-serif; padding: 40px; } .help-step { border-radius: 8px; background-color: #047ea9; color: white; font-size: 20px; padding: 20px; margin-bottom: 20px; } .icon { background-image: url({{ICON_URL}}); width: 76px; height: 76px; background-size: 76px 76px; border-radius: 15px; margin: 0 auto; } .share-icon { width: 32px; height: 27px; display: inline-block; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA2CAQAAADdG1eJAAAAyElEQVRYw+3YvQrCMBSG4a/SKxC8M6dOnQShk9BJKAi9OcFLKrwuaamDRRtMLHxnCBySch7yR6i07aCjy1seyEbgxhhd3vI5CKH8ddamJNAD0EoAEm1SQijfSCNAoklGoAcG6pAFgMQpCYELMFBN+QSQqBmA828BBx4cZ/kMIFFxZ5/2NLwA1sQu92VuQHZAubTB3vcVRfz4/5+BZfnnY5cPqjehAQYYYIABBhhQxn3+zYPFS2CAAQYYYIABBqx6kMT+htzADDwBk2GVUD9m13YAAAAASUVORK5CYII='); background-size: 32px 27px; vertical-align: -4px; } .icon-title { font-family: "Helvetica Neue", helvetica, sans-serif; text-align: center; font-size: 16px; margin-top: 10px; margin-bottom: 30px; } @media only screen and (max-width: 767px) { #help { padding: 30px 0px 10px 0px; } .help-step { padding: 10px; } } </style> </head> <body> <div id="help"> <div class="icon"></div> <div class="icon-title"> {{ICON_URL}} <div class="icon-title"> {{TITLE}} <div class="icon-title"> {{SHORTCUT_URL}} </div> <div class="help-step"> <strong>1.</strong> Tap the <div class="share-icon"></div>button in the toolbar </div> <div class="help-step"> <strong>2.</strong> Select "Add to Home Screen" </div> <div class="help-step"> <strong>3.</strong> Tap "Add" </div> </div><script type="text/javascript"> if (navigator.standalone) { window.location = "{{SHORTCUT_URL}}"; } else { var helpDiv = document.getElementById("help"); helpDiv.style.display = "block"; } </script> </body> </html> ''' redirect_template = '''<!doctype html> <html> <head> <meta charset="utf-8"> <title>Loading...</title> </head> <body> <script> window.location = "data:text/html;base64,{{DATA}}"; </script> </body> </html> ''' def main(): global page_template,redirect_template,redirect_page fields = [] field = {'title':'title','type':'text','value':''} fields.append(field) field = {'title':'icloud','type':'switch','value':False} fields.append(field) field = {'title':'script','type':'text','value':''} fields.append(field) field = {'title':'arguments','type':'text', 'value':''} fields.append(field) icon_url = 'https://i.imgur.com/en0NsLZ.jpg' field = {'title':'icon url','type':'text','value':icon_url} fields.append(field) f = myform_dialog(title='Home Screen Shortcut', done_button_title='ok',fields=fields, sections=None) #print(f) if f == None: # Close/cancel pressed return script = f['script'] #print(script) if 'Mobile Documents/iCloud' in script: icloud = True else: icloud = False t = 'Pythonista3/Documents/' i = script.find(t) script = script[i+len(t):-3]# remove path and .py arguments = f['arguments'] shortcut_url = 'pythonista3://' + script + '?action=run' if icloud: shortcut_url = shortcut_url + '&root=icloud' if arguments != '': shortcut_url = shortcut_url + '&' + arguments #shortcut_url = 'pythonista3://patience.txt' #shortcut_url = 'pythonista3://Affiches_films?action=run&root=icloud&argv=from_launcher' title = f['title'] icon_url = f['icon url'] #icon_url = 'https://i.imgur.com/gw1k9Dn.jpg' #icon_url = 'https://i.imgur.com/en0NsLZ.jpg' page = page_template.replace('{{TITLE}}', title).replace('{{SHORTCUT_URL}}', shortcut_url).replace('{{ICON_URL}}', icon_url) page_b64 = str(base64.b64encode(page.encode()),'utf-8') redirect_page = redirect_template.replace('{{DATA}}', page_b64).encode() httpd = http.server.HTTPServer(('', 0), MyHandler) port = str(httpd.socket.getsockname()[1]) webbrowser.open('safari-http://localhost:' + port) httpd.handle_request() class MyHandler (http.server.BaseHTTPRequestHandler): def do_GET(s): global redirect_page s.send_response(200) s.send_header('Content-Type', 'text/html') s.end_headers() s.wfile.write(redirect_page) def log_message(self, format, *args): pass class MyTextFieldDelegate (object): global c def textfield_should_begin_editing(self, textfield): if textfield.name == 'script': icloud = c.values['icloud'] py_file = file_picker_dialog('Select script', multiple=False, select_dirs=False,file_pattern=r'^.*\.py$',from_dialog=[c,textfield], icloud=icloud) if py_file != None: textfield.text = py_file c.values['script'] = py_file def textfield_did_end_editing(self, textfield): if textfield.name == 'icon url': try: url = textfield.text r = requests.get(url) c.container_view['icon'].image = ui.Image.from_data(r.content) except Exception as e: return def myform_dialog(title='', fields=None,sections=None, done_button_title='ok'): global c sections = [('', fields)] c = dialogs._FormDialogController(title, sections, done_button_title=done_button_title) y = 40 for s in c.cells: for cell in s: y = y + cell.height w = c.container_view.width h = c.container_view.height - y x = 0 icon = ui.ImageView(frame=(x,y,w,h)) icon.name = 'icon' icon.content_mode = ui.CONTENT_SCALE_ASPECT_FIT c.container_view.add_subview(icon) url = c.values['icon url'] if url != '': try: r = requests.get(url) icon.image = ui.Image.from_data(r.content) except Exception as e: console.hud_alert('download error '+url) for i in range(0,len(c.cells[0])): # loop on rows of section 0 cell = c.cells[0][i] # ui.TableViewCell of row i # some fields types are subviews of the cell: # text,number,url,email,password,switch # but check, date and time are not set as subviews of cell.content_view if len(cell.content_view.subviews) > 0: tf = cell.content_view.subviews[0] # ui.TextField of value in row # attention: tf.name not set for date fields if tf.name in ['script','icon url']: # No check for switch tf.delegate = MyTextFieldDelegate() # delegate to check while typing break c.container_view.present('sheet') c.container_view.wait_modal() # Get rid of the view to avoid a retain cycle: c.container_view = None if c.was_canceled: return None return c.values # Protect against import if __name__ == '__main__': main()
The file picker is based on @omz script File Picker.
As it is called by a form dialog, I've had to modify it because the scrolling did not work, perhaps due to two successive wait_modal.… … … def done_action(self, sender): self.selected_entries = [self.flat_entries[i[1]] for i in self.table_view.selected_rows if self.flat_entries[i[1]].enabled] if self.from_dialog != None: if self.selected_entries != None: paths = [e.path for e in self.selected_entries] c = self.from_dialog[0] tf = self.from_dialog[1] c.values[tf.name] = paths[0] tf.text = paths[0] self.view.close() def file_picker_dialog(title=None, root_dir=None, multiple=False, select_dirs=False, file_pattern=None, show_size=True,from_dialog=None,icloud=False): if root_dir is None: root_dir = os.path.expanduser('~/') if title is None: title = os.path.split(root_dir)[1] if icloud: root_node = FileTreeNode('/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/', show_size, select_dirs, file_pattern) # bug? does not use root_dir else: root_node = FileTreeNode(root_dir, show_size, select_dirs, file_pattern) # bug? does not use root_dir root_node.title = title or '' picker = TreeDialogController(root_node, allow_multi=multiple) picker.from_dialog = from_dialog picker.view.present('sheet') if from_dialog == None: picker.view.wait_modal() else: # called by a textfield of form_dialog. # if wait_modal, scroll does not function # return process performd in done_action, see above return if picker.selected_entries is None: return None paths = [e.path for e in picker.selected_entries] if multiple: return paths else: return paths[0]
-
-
-
@cvp, very interesting. I think I will be needing this soon.
-
Look very useful. When I run it, I get:
Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/home_screen.py", line 4, in <module>
import http.server
File "/var/containers/Bundle/Application/C1B2B29C-77ED-496F-8E3F-47352515D93C/Pythonista3.app/Frameworks/Py3Kit.framework/pylib/http/server.py", line 101, in <module>
import socketserver
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/site-packages/socketserver/init.py", line 7, in <module>
raise ImportError('This package should not be accessible on Python 3. '
ImportError: This package should not be accessible on Python 3. Either you are trying to run from the python-future src folder or your installation of python-future is corrupted -
-
@cvp You are quite right. I deleted that module and downloaded the file-picker module. The script runs but when I begin editing the script field in the dialog I get:
Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/B86CEE4E-E4A9-4BB4-9CA7-6E13BDA2C2A4/Pythonista3/Documents/home_screen.py", line 191, in textfield_should_begin_editing
py_file = file_picker_dialog('Select script', multiple=False, select_dirs=False,file_pattern=r'^.*.py$',from_dialog=[c,textfield], icloud=icloud)
TypeError: file_picker_dialog() got an unexpected keyword argument 'from_dialog' -
@ihf See the second part of my first post: I have modified the original File Picker module.
You have to also modify it as explained there. -
Ok, I modified File_Picker and the script now runs except that the shortcut url that is generated results in file not found. I think the problem may be this line:
shortcut_url = 'pythonista3://' + script + '?run'
If I change that to:
shortcut_url = 'pythonista3://' + script + '?action=run'
, it seems to work. Not sure how this worked for you without that.
Thanks for your help and a very nice script. I don’t suppose there is a way to automate the Add to Screen interaction using objc? -
@ihf You're right. I don't understand where this "action="is gone 😅
Sorry for that and thanks for your helpI don't think it would be possible to do an automatisation of the steps in Safari, but I'm not a big specialist of Objective-C