[Lab] Function to easily create a vertical view of views
Hmm, Sunday nights 😱 Ok, this is a function to create a vertical list of views. Granted, it's not difficult, but can be a pain. The below is pretty flexible if you want to get started quickly with a vertical layout. It's also orientation and size friendly. If you take out the comments and test code, it's pretty tiny I think. Anyway, I know I will be able to use something like this. I will tweak it a bit to be able to use a scrollview and clean it up.
# Pythonista Forum - @Phuket2 import ui, editor def simple_vert_view(parent, v_list, cv_margin=(0, 0), v_margin=(0,0), *args, **kwargs): ''' simple_h_view: Description - creates a number of ui.Views vertically. A root ui.View named 'cv' is created first. The vertical views are subviews of 'cv' params - 1. parent = the parent view that this view will be a subview of 2. v_list = a list of numbers. each number represents the height of the view. as follows 0 = fill the free space, if more than one, its divided <1 = is a percentage if the overall height avail >1 = is a abs points value 3. cv_margin, is used when creating the rect fir the 'cv' view. the 'cv' is equal to the parent.bounds.inset(*cv_margin). 4. v_margin = is called on the ui.rect that is used to create the view. The view is already sized as per the v_list. so this will change the dimensions of the view. not trying to go the other way, meaning adusting the abs values in the v_list, would not be hard to do, but this way makes more sense i think. 5. **kwargs = kwargs are evaluated for every view, including the 'cv' view. just a choice. returns: a ui.View. The view returned is named 'cv', contains the views created from v_list as subviews. each subview is named p0..pn ''' r = ui.Rect(*parent.bounds).inset(*cv_margin) cv = ui.View(name='cv', frame=r) cv.bg_color = 'pink' cv.flex = 'wh' def translate_height(ph, h): # i know can be done better... but thinking will expand this if h == 0 or h > 1: return h elif h == -1: return ph elif h < 1.0: return ph * h def apply_kwargs(kwargs, obj): for k, v in kwargs.items(): if hasattr(obj, k): setattr(obj, k, v) # translate the numbers in v_list to absolute numbers v_list = [translate_height(r.height, x) if x else x for x in v_list] # var is the space left over in the view after the absolute heights # have been subtracted divided by the number if items that are # equal to 0 zero_count = v_list.count(0) # aviod divide by zero error var = (r.height - sum(v_list)) / zero_count if zero_count else\ (r.height - sum(v_list)) # replaces 0 in v_list with var. v_list = [var if x == 0 else x for x in v_list] y = 0 # keep track of the height # go through v_list, create the views as subviews of cv, and apply # some attrs to the created views for i, h in enumerate(v_list): frame = ui.Rect(0, y, r.width, h).inset(*v_margin) v = ui.View(name='p' + str(i), frame=frame) v.flex = 'whlrtb' apply_kwargs(kwargs, v) cv.add_subview(v) y += h # hmmm, to do or not do? apply_kwargs(kwargs, cv) return cv class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.make_view() def make_view(self): v_list = [44, 0, 0, 0, 44, .5, 60] #v_list = [44, 0, 60] #v_list = [.5, .4, 0, 0] #v_list = [44, .2, 0, 60] #v_list = [.5, .5] #v_list = [48, .4, 0, 64] v = simple_vert_view(self, v_list, cv_margin=(10, 10), v_margin=(5, 5), corner_radius=5, border_width=.5) self.add_subview(v) if __name__ == '__main__': _use_theme = True w, h = 600, 800 f = (0, 0, w, h) style = 'sheet' mc = MyClass(frame=f, bg_color='white') if not _use_theme: mc.present(style=style, animated=False) else: editor.present_themed(mc, theme_name='Oceanic', style=style, animated=False) # accessing a view, different ways mc['cv']['p0'].bg_color = 'crimson' cv = mc['cv'] cv.subviews[len(cv.subviews)-1].bg_color = 'cornflowerblue'
A small breakthrough in thinking. I created a split view horizontal function below. I have it working, but need to quite a bit more to clean it up and follow same API as the vertical. But almost 1am here now, time to go home eat some food and watch some series until,I pass out 😱💅🏽
Will,finish this tomorrow, well actually it tomorrow already
def split_view_hor(parent, v): v.width = v.width / 2 r = ui.Rect(*v.frame) r.x = r.max_x nv = ui.View(frame = r ) nv.flex = v.flex nv.bg_color = 'yellow' v.superview.add_subview(nv)
Ok, in my mind I have actually done something good now. I am guessing I have thought this before.
I don't think it's finished yet. But it works very well in my opinion.
So, it's just about creating a view of views. Which you basically always need. In this case, you call a function and pass a list of numbers to describe the vertical view, which returns a ui.View with the vertical views as subviews.
Then if you need any of those vertical views to x number of views horizontally then you call another function that basically splits the view in place so to speak.
Yes, I know some are thinking already, why 2 functions. I have struggled with this so many times in the past. Meaning how to do this in some sort of easy meaningful way. For me this so easy, to create the vertical view, then split the vertically views horizontally. The Params can be kept super simple. And the code doesn't get too twisted and complicated (maybe some will prove me wrong on this).
But I do see that with a horizontal and vertical splitter function, you could make any view in a few mins. I will work on this next. Just wanted to get this far first.
Also it's worth nothing that everything is orientation and size friendly, well what I have tested has been. Takes a long time to test all the variations. Also it's all running off flex , which simplifies again. The hard work is left to ui.
Anyway the gist is below. I still see it as work in progress, improvements can and will be made. Again, I am really happy with this. It's not as the crow flys from a to b, but it's dead simple to use in my mind.
Well life moves on. I think this gist Is a whole lot better. Now, I have 2 main functions that can easily build most common types of view layouts very easily. Amazing how simple it is. It's still work in progress. One big issue is finding a uniform way to access views. Using @JonB ViewWalker code I will find a way to do this. But now, the 2 main functions are insert_into_view and split_view_h. insert_into_view is nice as it can insert vertically or horizontally. split_view_h only splits a view horizontally, not sure it needs to split vertically. Vertically, you would use insert_into_view. It's still,a gist because more to do, but I am excited to share what I have anyway.
The below view can be made with very little effort both in code and thought.
Made more improvements today. The biggest dealing with the view names. I added a parameter to the 2 main functions to accept a list of names. I think I have done this in quite flexible way. I have commented the code. but this was a big thing missing, I think my approach is a pretty reasonable solution.
I also added @JonB ViewWalker Class. it's very helpful. Most notably in MyClass (Example implementation ). You can easily access all your views with dot notation via one class attr. I have commented on this in the code.
I did some other things such as a dynamic attr to the views created in the module called vid (View ID). Maybe not necessary, but it's still helpful for the sake of a few bytes per view.
One thing I am conflicted about is the code similarities between the 2 main functions. is so tempting to try and merge them, but my gut feeling is this would be a mistake. Well for me it would be a mistake, lets say that. if I tried to combine them, I would be worried about really weird logic creeping in. I should be able to remove some of it into a different function that both funcs call. baby steps....
I have a few things directly on my mind that I need to think about.
- when splitting a view, should the view being split be renamed. in a practical sense, it seems so. but this can make things a little ambiguous also.
- when splitting a view, should you inherit the visual aspects of the parent? Or maybe support kwargs, apply to the parent as well as the new views. The latter is starting to make more sense.
- Does split_view_h need to be able to split vertically as well.
A few other smaller issues to consider also.
Again, I know for a lot, this is not exciting. For me though, it is. it's only a wireframe so to speak. But getting wirefames in place to start populating them with tables/controls etc can be so frustrating and time consuming. and it's about the least exciting thing you have to do. if you can just knock out the wireframe quickly and start adding your functionality, to me that's great.
Btw, the code looks long. it's not. it's just all the commentary.
New gist Here.