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.


    Button freezes Pythonista

    Pythonista
    4
    18
    8856
    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.
    • techteej
      techteej last edited by

      If you click the Microphone button in my program, it automatically freezes Pythonista. Any ideas?

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

        From the ui docs:

        One important thing to keep in mind when implementing actions and delegate methods is that all callbacks are called on the main (UI) thread of the host app. If your function takes a long time to return, the app will appear “frozen” during that time. For delegate methods that are expected to return a value, this is required for the underlying interaction with UIKit. Other callback methods can be decorated to run in the background (on the regular interpreter thread) using @ui.in_background.

        Decorate your action as in the example... That solves many problems where the ui locks up.

        Actually, you may need to use ui.in_background(server.serve_forever), rather than at the action level. Not sure.
        Not sure, but you might also want a 'ui.delay` to load the url, so that the url gets loaded after the server started.
        In the example you wer using, it launched safari, which took a while, and the serve command executed while safari was still loading. Here, not sure if load_url blocks until the url is loaded, in which you'd get a server not found error before starting the server.

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

          ui.in_background(server.serve_forever) does not work

          1 Reply Last reply Reply Quote 0
          • ?
            A Former User last edited by

            @techteej That's good progress you've made, well done. I would change it a little... the extra def in the class will suppress the log messages... randomising the port will let it run multiple times without the "port in use" error... the ui.delay is a trick that seems to help (I think it's to do with the threading issue and maybe cedes control to let other things catch up). Of course you can go on to adapt the HTML to give a better title and button names. You might even be able to automate the upload using evaluate_javascript on your WebView.

            import speech, ui
            from BaseHTTPServer import BaseHTTPRequestHandler
            import urlparse
            import urllib
            import cgi
            import editor
            import console
            from socket import gethostname
            import os, webbrowser
            from cStringIO import StringIO
            
            lang = 'en-GB'
            speed = 0.1
            
            def brit_switch_action(sender):
            	global lang
            	lang = 'en-GB' if sender.value else 'en-US'
            	if sender.value:
            		v['au_switch'].enabled = False
            	else:
            		v['au_switch'].enabled = True
            
            def aus_switch_action(sender):
            	global lang
            	lang = 'en-AU' if sender.value else 'en-US'
            	if sender.value:
            		v['brit_switch'].enabled = False
            	else:
            		v['brit_switch'].enabled = True
            
            def slider_action(sender):
            	global speed
            	speed = v['slider1'].value
            
            def button_speak_action(sender):
            	global speed
            	text = v['user_text'].text
            	if text == 'Enter your text here':
            		speech.say('Please tell me something to say.', lang, speed)
            	else:
            		speech.say(text, lang, speed)
             
            TEMPLATE = ('<!DOCTYPE html><html><head>' +
              '<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/3.2.0/'+
              'css/bootstrap-combined.min.css" rel="stylesheet"></head><body>' +
              '<div class="container">' +
              '<h2>Upload File</h2>{{ALERT}}'
              '<p><form action="/" method="POST" enctype="multipart/form-data">' +
              '<div class="form-actions">' +
              '<input type="file" name="file"></input><br/><br/>' +
              '<button type="submit" class="btn btn-primary">Upload</button>' +
              '</div></form></p><hr/>' +
              '</div></body></html>')
             
            class TransferRequestHandler(BaseHTTPRequestHandler):
            	def get_unused_filename(self, filename):
            		if not os.path.exists(filename):
            			return filename
            		basename, ext = os.path.splitext(filename)
            		suffix_n = 1
            		while True:
            			alt_name = basename + '-' + str(suffix_n) + ext
            			if not os.path.exists(alt_name):
            				return alt_name
            			suffix_n += 1
            		
            	def do_GET(self):
            		parsed_path = urlparse.urlparse(self.path)
            		path = parsed_path.path
            		if path == '/':
            			html = TEMPLATE
            			html = html.replace('{{ALERT}}', '')
            			self.send_response(200)
            			self.send_header('Content-Type', 'text/html')
            			self.end_headers()
            			self.wfile.write(html)
            			return
            		file_path = urllib.unquote(path)[1:]
            		if os.path.isfile(file_path):
            			self.send_response(200)
            			self.send_header('Content-Type', 'application/x-python')
            			self.send_header('Content-Disposition',
            			                 'attachment; filename=%s' % file_path)
            			self.end_headers()
            			with open(file_path, 'r') as f:
            				data = f.read()
            				self.wfile.write(data)
            		else:
            			self.send_response(404)
            			self.send_header('Content-Type', 'text/html')
            			self.end_headers()
            			self.wfile.write(html)
             
            	def do_POST(self):
            		form = cgi.FieldStorage(fp=self.rfile, headers=self.headers,
            		                        environ={'REQUEST_METHOD':'POST',
            		                       'CONTENT_TYPE':self.headers['Content-Type']})
            		self.send_response(200)
            		self.send_header('Content-Type', 'text/html')
            		self.end_headers()
            		field_item = form['file']
            		uploaded_filename = None
            		dest_filename = None
            		file_data = field_item.file.read()
            		file_len = len(file_data)
            		uploaded_filename = field_item.filename
            		dest_filename = self.get_unused_filename(uploaded_filename)
            		with open(dest_filename, 'w') as f:
            			f.write(file_data)
            		editor.reload_files()
            		del file_data
            		html = TEMPLATE
            		if uploaded_filename != dest_filename:
            			message = '%s uploaded (renamed to %s).' % (uploaded_filename,
            			                                           dest_filename)
            		else:
            			message = '%s uploaded.' % (uploaded_filename)
            
            	######
            	def log_message(self, format, *args):
            		pass 
            	######
            	
            def record_action(sender):
            		######
            		sender.superview['webview1'].hidden = False
            		######
            		#console.clear()
            		#from BaseHTTPServer import HTTPServer
            		#server = HTTPServer(('', 8080), TransferRequestHandler)
            		#URL = 'http://localhost:8080' 
            		#webbrowser.open('http://localhost:8080', stop_when_done = True)
            		#webview = v['webview1']
            		#webview.load_url('http://localhost:8080')
            		#server.serve_forever()
            
            v = ui.load_view('speech')
            v['au_switch'].enabled = False
            v['au_switch'].value = False
            
            speech.say('Greetings!', lang, 0.1)
            v.present(style='full_screen', hide_title_bar=True)
            
            from BaseHTTPServer import HTTPServer
            ######
            server = HTTPServer(('', 0), TransferRequestHandler)
            def f():
            	pass
            gport = server.server_address[1]
            ui.delay(f,0)
            webview = v['webview1']
            webview.hidden = True
            webview.load_url('http://localhost:'  + str(gport))
            server.serve_forever()
            ######
            
            1 Reply Last reply Reply Quote 0
            • ?
              A Former User last edited by

              @techteej Here's some snippets to get you going with automating the upload... give the file button an id in the HTML... then you can leave the WebView hidden and click the file button automatically from your microphone button. Another nice thing to do would be to make the app close down smoothly by stopping the server... you could do that by subclassing the View and stopping it in the will close.

              '''
              '<input id="file" type="file" name="file"></input><br/><br/>' +
              '''
              
              def record_action(sender):
              		######
              		#sender.superview['webview1'].hidden = False
              		sender.superview['webview1'].evaluate_javascript('document.getElementById("file").click()')
              		######
              
              
              1 Reply Last reply Reply Quote 0
              • ?
                A Former User last edited by

                @techteej I've confirmed that it's possible to complete the automation by checking for the video capture to complete and automatically clicking the upload button when it does... all with the WebView hidden. I'll post the code here if you want.

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

                  @tony Yes, please. As of right now when I press take photo or video it just crashes.

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

                    @tony, instead of pasting your code here, it would be best if you edited the program at https://github.com/tjferry14/Fun-Pythonista-Tools/blob/master/speech.py and the submitted a "pull request". That way we can all be looking at the same version.

                    1 Reply Last reply Reply Quote 0
                    • ?
                      A Former User last edited by

                      @ccc I'll maybe let techteej to do that when he's happy with his version.

                      @techteej Import sound (just temporarily while you're testing, the beep will confirm the script is waiting for the video file) and then give ids to the form and submit button of form and submit. Then use this in your microphone button...

                              sender.superview['webview1'].evaluate_javascript('document.getElementById("file").click()')
                              def loop():
                      			if sender.superview['webview1'].evaluate_javascript('document.forms["form"]["file"].value') == '':
                      				sound.play_effect('Beep')
                      				ui.delay(loop,2)
                      			else:
                      				sender.superview['webview1'].evaluate_javascript('document.getElementById("submit").click()')
                               loop()
                      
                      1 Reply Last reply Reply Quote 0
                      • techteej
                        techteej last edited by

                        @tony This still crashes Pythonista.

                        1 Reply Last reply Reply Quote 0
                        • ?
                          A Former User last edited by

                          @techteej It works here on iPad mini (original). If it doesn't work for you (from a clean start of Pythonista) then it may be to do with the threading issue. It's only a guess why the ui.delay of an empty function for zero time helps, all we can say for sure is 'it does because it does' on one device anyway.

                          When Pythonista gets into the threading issue... one run of a script is not necessarily the same as a second... so it's worth to try from a clean start of Pythonista, (if you haven't already). Otherwise I don't know what to say except it must be frustrating (been there!)

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

                            I am struggling to understand this code. get_unused_filename() and do_POST() never get called.

                            do_GET() only gets called once with self.path set to '/'.

                            What is supposed to happen after the user clicks "Use Video"?

                            1 Reply Last reply Reply Quote 0
                            • ?
                              A Former User last edited by

                              @ccc That part is basically @omz's file transfer script. The flow goes...

                              First GET to the server is for the HTML.

                              The POST will happen when the video file is being submitted... maybe you haven't got to that point. In turn it will call 'get unused file name' to get a free one if the name of the uploaded file is in use.

                              iOS will pop up a choice to take a new photo/video or choose an exiting one... any of the choices are ok, and whatever file results will be uploaded, when the user (or the JavaScript) clicks the submit button.

                              If you're using the JavaScript the user will never see the web page or the buttons, just the iOS pop up, which is nice and clean.

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

                                So when you click "Use Video" does do_Post() get called?

                                1 Reply Last reply Reply Quote 0
                                • ?
                                  A Former User last edited by

                                  @ccc Yes... when the user (or the JavaScript) clicks the submit button. If you're using the JavaScript the user will never see the web page or buttons, just the iOS popup, which is nice and clean.

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

                                    When I check the code out of the repo and run it and tap the record button and select "video" and record a video and tap "Use Video" nothing happens. There is no video file stored in the local directory. The beep in the background continues every 2 seconds until Pythonista times out.

                                    Are you using the current code in the repo? What is the name and full path of the video file that gets saved?

                                    1 Reply Last reply Reply Quote 0
                                    • ?
                                      A Former User last edited by

                                      @ccc The repo is missing the step of adding ids to the form and submit button of form and submit respectively. The file is saved to the same directory as the script. For a video it's capturedvideo.MOV.

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

                                        I just submitted a pull request with the two ids added.

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