[Lab] UIFiles referencing other UIFiles when loaded as Custom UI Objects
Phuket2 last edited by
This is possibly very stupid. It can be hard to know sometimes. The code below does not run as the pyui files are not included. It's more about the idea. I have added a pic to help explain, it's also pretty crappy 😱
Btw, I am going to stop using pyui to refer to a Pythonista designer file. Its listed as a UIFile, so I will call it that.
It's not that easy to explain, but I will try.
So, we can create a custom view class from a UIFile. nice. All the magic happens in ui.load_view plus the help of the pyui_bindings func from @JonB. This works extremely well now as @JonB perfected it to deal with custom attributes, globals etc....
So what if you want to make a new view that uses one or more of your coded classes that read from a UIFile. And the way you want to do it is in the new UIFile, you just want to a a custom view and set it to code object (hope that's clear). So in your new UIFile, you specify a root Custom View Class, which you provide the code for in your .py file. Then for your objects, you create a custom view in the designer, set its Custom Class View to your existing code object for that UIFile and they should magically appear.
As I said not easy to explain.
Well, if you have read this far, the kicker is I didn't resolve it 😂 I did a hack, inserted a constant (at least at the right place) not sure anything better can be done without @omz making some tweaks to ui.load_view.
Again, I am not sure this is important or not. I just have a feeling it could be. I am sure in some ways this is just easier by loading UIFiles into a view and then manipulating them. While, I am sure that's true, I think design wise reading/coercing a UIFile into a Custom class is so much better. Keeping in mind, I am still a self confessed newbie. My hope is that eventually, time permitting @omz will build this into ui anyway.
Anyway, below is some code and pic to show my meaning. I am showing the same view 4 times in the composition view, but the idea is that it could be any number of views made up of any number of different UIFiles.
import ui, editor from random import choice ''' Pythonista Forum - @Phuket2 ''' _themes = ['Dawn', 'Tomorrow', 'Solarized Light', 'Solarized Dark', 'Cool Glow', 'Gold', 'Tomorrow Night', 'Oceanic', 'Editorial'] def pyui_bindings(obj): # JonB def WrapInstance(obj): class Wrapper(obj.__class__): def __new__(cls): return obj return Wrapper bindings = globals().copy() bindings[obj.__class__.__name__]=WrapInstance(obj) return bindings class UIFileBase(ui.View): def __init__(self, ui_file, *args, **kwargs): ui.load_view(ui_file, pyui_bindings(self)) super().__init__(*args, **kwargs) def add(self, p): # do like this so we can add transformations here later p.add_subview(self) class Panel(UIFileBase): def __init__(self, ui_file = None, *args, **kwargs): # hmmm, when a pyui file references this class # what comes first, the chicken or the egg? if not ui_file: ui_file = 'title_panel' # horrible, a constant here super().__init__(ui_file, *args, **kwargs) print('in Panel') class Combo(UIFileBase): # composite of other UIFiles def __init__(self, ui_file=None, *args, **kwargs): super().__init__(ui_file, *args, **kwargs) class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if __name__ == '__main__': _use_theme = True w, h = 400, 400 f = (0, 0, w, h) ui_file = 'title_panel' style = 'sheet' animated = False mc = MyClass(ui_file, frame=f, bg_color='white') if not _use_theme: mc.name = 'No Theme' mc.present(style = style, animated=animated) else: theme = choice(_themes) mc.name = theme editor.present_themed(mc, theme_name=theme, style=style, animated=animated) ''' pf = ui.Rect(*mc.bounds.inset(20, 20)) pf.height = 150 v = Panel(ui_file, frame = pf ) v.add(mc) ''' ui_file = 'comp' pf = ui.Rect(*mc.bounds.inset(5, 5)) p = Combo(ui_file, frame = pf) p.add(mc)
JonB last edited by
@Phuket2 Not sure I understand what you are tying to do...
Why would you accept ui_file as an argument to Panel?
It seems to me once you specialize a UiFile into a Panel, you know the pyui associated with Panel. A Panel is not any pyui, once tou have specialized it from UIfile, it is exactly a
title_panel.pyui. So just eliminate ui_file from the Panel constructor, and call super().init('title_panel.pyui', ...)
Now, if you wanted to subclass Panel, and use a different pyui, that gets trickier. You would have to craft the subclass pyui very carefully so that you are just adding the new functionality. but i don't think you are trying to do that here.
Alternatively, UIFile should be an abstract class which requires subclasses to define ui_file(). Then to override Panel, you would override that method.
Phuket2 last edited by
@JonB , what I was attempting to do which works btw, it's just ugly.
For Panel class, if I remove the lines
if not ui_file:
ui_file = 'title_panel' # horrible, a constant here
I have a Panel class, that is basically defined by the UIFile. In this case 'title_panel.pyui'. We know that works perfectly.
Then I create another UIFile called combo.pyui. I load that into a class called Combo.
The combo UIFile only contains 4 custom views. v1, v2 etc... each of the custom views has its Custom View Class set to Panel (referring to the Panel Class in the code). So now instead of me loading the Panel class, it's coming from the loading of the UIFile Combo. As far as I can see, I don't have a way to pass the Panel function a param from the UIFile.
But, look maybe it's not the smartest of ideas. I was Justin's thing how to get the most out of this. Meaning being able to use title_panel.pyui and it's code class either as a single ui element, or as a sub element in another view. Aka modular use of UiFiles.
Anyway, the combo.pyui took me a minute or so to put together. Created a custom view, set the sizes, flex, the custom view class, copy and pasted it 4 times and position the views. Presto was working. As all the flex settings for the Panel all done correctly, it all behaves as expected.
Also, even if what I have done is crappy, I think it still illustrates some of the improvements that can be made to the ui. I understand, there are many aspects to Pythonista, ui just being one of them. Hopefully trying things like this will help keep it high on the priority list 😬
I really hope @omz some how incorporates your binding code into the ui module when he gets a chance. I think it would be a great boost for ui usage in general.