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
-
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?
-
You can't set a button name as an action. You need to set each individual button action.
-
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?
-
Solved it!
the_action = getattr(self, name + '_action') button.action = the_action
-
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()
-
Thanks @ccc
-
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?
-
-
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.
-
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
-
If I remove the decorator
@ui.in_background
I get an error:
TypeError: Cannot show camera from main UI thread
-
@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.
-
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.