Copying UI editor object programmatically
Is there a clean way to make a ui.Button for example in the UI editor and then make a object (or copy) of it programmatically?
If so this would be cool because I could make the object quickly the first time in the editor, then use it many times through out the code.
Well this is all interesting stuff.... The copy module is extremely useful.
@Gcarver, you brought up a great point for me with the copy. I have a custom object inheriting from ui.View. If that object, I create a lot of other views and buttons. Just positioning the views and buttons and assigning attributes.
If another custom class I create 31 of these objects in a for statement. It's a bit sluggish. I was going to go back later and see what I could to to optimise it. But when I seen your post about copy, I dawned on me to try creating one object and just do copies instead. Massive speed increase, except it doesn't really work. I am pretty sure my code is working, was just a little rework.
I can see that new objects are being created, but I don't see my object on screen. I read a little about it and can see that maybe I need to use deepcopy instead of copy. I tried using deepcopy, the function just never return. Was hoping you or someone else have any ideas?
I can see that new objects are being created, but I don't see my object on screen.
Are you changing
my_ui_item.yso it is not hidden underneath the original?
@ccc, yes I am changing all the x/y cords. I will do some more checking. Mabe I have made a silly mistake. I have a placement method to do this. I only changed the creation point in the code. I will look further, also change it back. But if it's really working, timed code around the for loop went from 0.358 to 0.02, a very exciting increase in speed
Are you doing:
print(my_ui_item.frame)to ensure there are no zero values.
Is your copy routine throwing exceptions? (Surround it with a try/except block)
@ccc, I have listed the creation code of the objects below. Also links to pics of the results on screen. But after the creation code nothing changes. Something must be gapping in the copy code unless I have made a stupid blunder, which is highly possible, I hope I have...
''' Create 31 day objects only once, dont display yet. put the day objects in their own view. TODO: ''' v = ui.View(frame = self.frame) v.height -= cp.wb_height v.y = cp.wb_height v.background_color = cp.dv_bg_color w = self.width / 7 h = w * cp.di_height_ratio print w,h self.day_height = h self.day_width = w _USE_COPY = True if _USE_COPY: start = time.time() d_item = day_item(i, w,h) d_item.day_action(self.day_button_click) for i in range(1,32): try: new_item = copy.copy(d_item) new_item.day_title = i v.add_subview(new_item) self.day_obj.append(new_item) except: print 'a problem' self.add_subview(v) self.days_view = v finish = time.time() print finish - start else: start = time.time() for i in range(1,32): d_item = day_item(i, w,h) d_item.day_action(self.day_button_click) v.add_subview(d_item) self.day_obj.append(d_item) self.add_subview(v) self.days_view = v finish = time.time() print finish - start
Result when using copy
https://www.dropbox.com/s/mi16axib4l5ccyk/file 22-06-2015 18 14 31.png?dl=0
Result when not using copy
https://www.dropbox.com/s/kqnq526o3q54d30/file 22-06-2015 18 15 06.png?dl=0
I should mention that the above code is executed in the init function of my class, in case that has some weird affect on things.
Also, @ccc, your copy_button.py shed some light on some things for me. Mainly how to step over non attributes in the ui classes. But really nice to know how to do it.
One could say should read all the sample code around, but my brain is not as sharp as it used to be. Also, as I learn more python syntax , the code makes more and more sense to me. So will look more, especially at yours and @JonB code on github. Thanks again guys, is great!
don't put this in init, because frame might not yet be created, unless you pass it in explicitly. at a minimum, frame will get changed as soon as you present it, so unless you have flex set up properly, things will not scale as you expect.
actually, id recommend you implement a
resizefunction in your custom view, which relays out the features when rotation occurs for example, which might address the issue.
@JonB, thanks. Not sure this will really fix my problem. However, it's about time I got serious about orientation etc... I sort of thought I had it figured out, and to my surprise I don't :) I am not really surprised. I started off with a small simple class for drawing the days of the week across the top of the screen. Just using buttons in a view. If I create the view without it being a subview of another view, I am getting called on the layout method as expected. However as soon as I add the custom view as a subview to another view the layout method is called twice on start up and no more calls to layout arrive as I change the orientation of my iPad.
The only code difference I have in the custom class is that if it has a superview I set the customs class frame to the bounds of the superview. This seems to be the correct thing to do. But I tried many variations without success. Sorry to bother you, if it's a stupid thing I have done here, I have really tried to figure it out myself without any success.
import ui week_days = ['MON','TUE','WED','THU','FRI','SAT','SUN'] class day_title_bar(ui.View): def __init__(self): self.day_titles= #create the 7 buttons that will be day headings for i in range(0,7): btn = ui.Button(title = week_days[i]) self.day_titles.append(btn) self.add_subview(btn) #set all the style attributes for the view self.style() def layout(self): # check to see if the view has a superview #if its added as a subview, it will have # a superview, otherwise it wont have if self.superview: self.frame = self.superview.bounds w = self.width / 7 for i in range(0,7): btn = self.day_titles[i] btn.width = w btn.height = 60 btn.x = i * w def style(self): self.background_color = 'white' for i in range(0,7): btn = self.day_titles[i] btn.background_color = 'red' btn.tint_color = 'white' btn.font = ('<system>',18) if __name__ == '__main__': __ADD_AS_SUB_VIEW = True if __ADD_AS_SUB_VIEW: # this way, the custom classes layout method # is only called twice, both at start up # as the ipad orientation changes, # i dont recieve any more layout method calls v = ui.View() x = day_title_bar() v.add_subview(x) v.present() else: # this works as expected. layout is called # when the ipad orientation changes v = day_title_bar() v.present()
x.flex = 'WH'
@ccc, thanks. I want to chew off my own arm at the moment. God, the amount of time I sent on trying to get it correct myself. I was not thinking out of the box. I would have thought that these events would have bubbled up all the parent views. But I guess this sort of explicit way saves redundant callbacks. But still would be nice to register the specific callbacks you would like to receive :)
I added the self.flex = 'WH' to the init code of the custom class. I check the various ways of creating the class and all seems ok.
Thank you :)
@ccc, @JonB, the old saying the more you know the less you know is coming in to play :) but going back refactoring my crappy code to be orientation friendly. Is quite a journey. But a good one, better I do it now and understand it than further down the track. You start to see some cool things happen. Like a form of double buffering. I am not really sure what's going on, but I guess from the start of calls to layout to the completion, all screen writes are being double buffered. Well, that's my old name for it anyway. If that's in the documentation, I didn't see it. But I think an addendum on the virtues of writing custom classes would be great. It's becoming very clear to me now that even the simplest ui tasks are simplified by custom classes.