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.


    ADD shortcut to Home Screen

    Pythonista
    3
    10
    7392
    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.
    • cvp
      cvp last edited by cvp

      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]
      

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

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

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

            @cvp, very interesting. I think I will be needing this soon.

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

              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 1 Reply Last reply Reply Quote 0
              • cvp
                cvp @ihf last edited by cvp

                @ihf I don:t have this error but I see here that you already got this kind of error some months ago...

                Edit: I don't have a socketserver folder in site-packages

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

                  @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'

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

                    @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.

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

                      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?

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

                        @ihf You're right. I don't understand where this "action="is gone 😅
                        Sorry for that and thanks for your help

                        I 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

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