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.


    Python string object as callable method

    Pythonista
    5
    13
    15993
    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.
    • Kipekeedev
      Kipekeedev last edited by Kipekeedev

      I'm having a problem concatenating a string to a variable to create a method in my app. It goes something like this:

      # coding: utf-8
      
      import ui
      
      w,h = ui.get_screen_size()
      buttons = ''' Scan_View Show_View '''.split()
      
      class OCRApp(ui.View):
      	def __init__(self):
      		x,y,w,h = self.bounds
      		self.background_color = 'orange'
      		self.present()
      		for i, button in enumerate(buttons):
      			button = str(button).lower()
      			self.add_subview(self.make_button(button,i))
      		
      	def scan_view_action(self, sender):
      		scanview = ui.load_view('scanview.pyui')
      		scanview.background_color = 'red'
      		scanview.present()
      		
      	def show_view_action(self,sender):
      		pass 
      	
      	def make_button(self,name, i):
      		button = ui.Button(title=name)
      		**the_action = name
      		button.action = the_action()**
      		button.center =w/2, (i*60)+(button.height*2)
      		self.add_subview(button)
      		return button
      
      OCRApp()
      	
      

      When I create a string on the variable 'the_action' and call it on 'button.action' I get an error ' TypeError: "str" object is not callable'.

      How do I go about doing this correctly?

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

        You can't set a button name as an action. You need to set each individual button action.

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

          @techteej

          Would this work?

          def scan_view_action(self,sender):
              pass
          
          def make_button(self,name):
              the_action = name + '_action'
              button.action = the_action
          

          Or is there any convenient way to do this to keep it DRY?

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

            Solved it!

            the_action = getattr(self, name + '_action')
            button.action = the_action
            
            1 Reply Last reply Reply Quote 0
            • ccc
              ccc last edited by ccc

              The code above calls .add_subview() twice for each button which will put four buttons on the screen instead of just two. A minimal implementation:

              # coding: utf-8
              
              import ui, console
              
              w, h = ui.get_screen_size()
              buttons = 'Scan_View Show_View'.split()
              
              class OCRApp(ui.View):
                  def __init__(self):
                      self.background_color = 'orange'
                      self.present()
                      for i, button in enumerate(buttons):
                          self.add_subview(self.make_button(button.lower(), i))
                      
                  def scan_view_action(self, sender):
                      console.hud_alert('scan')
                      
                  def show_view_action(self, sender):
                      console.hud_alert('show')
                  
                  def make_button(self, name, i):
                      button = ui.Button(title=name)
                      button.action = getattr(self, name + '_action')
                      button.center = w / 2, i * 60 + button.height * 2
                      return button
              
              OCRApp()
              
              1 Reply Last reply Reply Quote 0
              • Kipekeedev
                Kipekeedev last edited by

                Thanks @ccc

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

                  I know this is out of topic but I'm having trouble running this code. It freezes my app:

                  @ui.in_background
                  	def scan_document(self, sender):
                  		
                  		img = photos.capture_image()
                  		console.show_activity()
                  		if not img:
                  			return
                  		with io.BytesIO() as bIO:
                  			img.save(bIO, 'PNG')
                  			imgOut = ui.Image.from_data
                  

                  I don't know if you guys know a lot about the photos module but I can't find any good documentation.

                  @ccc do you have a github post on it?

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

                    http://omz-software.com/pythonista/docs/ios/photos.html

                    https://github.com/Pythonista-Tools/Pythonista-Tools/blob/master/Graphics and Imaging.md

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

                      do you have another in_background that is running? i tried your code standalone, and it works ok for me on the 1.6 beta.

                      I do recall similar porblems on the 1.5, which were solved by not using in_background, but then having the logic that shows the capture phoo dialog called in a ui.delay.

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

                        I will ask the obvious , do you really need to do Background processing ? But I understand, you should be still be able to background process if possible. But sometimes linear processing is reasonable given the task. If I am off track, ignore my comments

                        Kipekeedev 1 Reply Last reply Reply Quote 0
                        • Kipekeedev
                          Kipekeedev @Phuket2 last edited by

                          @Phuket2

                          If I remove the decorator

                          @ui.in_background
                          

                          I get an error:

                          TypeError: Cannot show camera from main UI thread
                          
                          
                          1 Reply Last reply Reply Quote 0
                          • Phuket2
                            Phuket2 last edited by

                            @Kipekeedev, ok I should not be commenting because it's outside my comprehension. But, if you look at what the decorator is doing might give you a better insight to what is happening. I am not trying to be smart, but I have fell into this trap before. You have a decorator called ui.in_background somehow it has some special and magical meaning. But it doesn't. If you look at the root of what the decorator is doing ( in this case it's about threads). Again I am talking above what I know exactly, but I think my idea is correct. Hope what I have said is not misleading.

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

                              The built in dialogs (like console.input_alert, raw_input, camera input dialogs, etc) need to be run in a background thread, though in 1.5 at least, there were some cases where in_background didn't work... possibly because of the fact that everything which uses in_background shares the same queue. I think there was some tuning of priorities in 1.6 (your code worked okay for me, but perhaps we need to see the entire app)

                              Using ui.delay is a simple way to spawn a new independent thread, without having to learn about the threading module. You would do something like

                              def scan_document(self, sender):
                                  def show():  
                                      img = photos.capture_image()
                                      console.show_activity()
                                      if not img:
                                          return
                                      with io.BytesIO() as bIO:
                                          img.save(bIO, 'PNG')
                                          imgOut = ui.Image.from_data
                                          # actually DO something with the image
                                ui.delay(show,0.1)
                              

                              if that still doesn't work, try first closing the view, before the ui.delay, then increase the delay time to a second or so, to give the view time to minimize. at the end of show you could represent the top level view. Again, i did not have to do any of this in 1.6, but sometimes such things were needed in 1.5.

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