• Thank you both! It works now

  • @JeremyMH a foundation vote that proposed @enceladus but without the editor UI

    import ui class StopWatch(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.value = 0 self.state = 'stop' self.update_interval = .1 def draw(self): t0 = (self.value // (600 * 60), self.value // 600, self.value // 10) t1 = (t0[0], t0[1] % 60, t0[2] % 60) ui.draw_string( "{:02}:{:02}:{:02}".format(*t1), font=('Helvetica', 20), rect=(150, 0, 0, 0), color='black', alignment=ui.ALIGN_CENTER) def update(self): if self.state == 'run': self.value += 1 self.set_needs_display() def button_action(sender): v1 = sender.superview['Watch'] sender.hidden = True if sender.title == 'Reset': v1.value = 0 v1.state = 'stop' sender.superview['Start'].hidden = False elif sender.title == 'Start': v1.value = 0 v1.state = 'run' sender.superview['Stop'].hidden = False elif sender.title == 'Stop': v1.state = 'stop' sender.superview['Reset'].hidden = False v = ui.View(width=397, height=271) v.add_subview(StopWatch(name = 'Watch', frame = (0, 30, 345.00, 76.00))) v.present('sheet') for btn in ['Reset', 'Stop', 'Start']: v.add_subview(ui.Button(frame=(v.center.x-40, v.center.y-40, 80, 80), name=btn, border_width=1, corner_radius=40, action=button_action)) v[btn].title = btn if btn != 'Start': v[btn].hidden = True```
  • Ok, let's start very basic.

    Your views start at the root view -- the one you call present on -- and then are heirarchical going down . If you assign a name in the UI editor, you can refer to a subview by name, a but only from it's immediate superview.

    Please do the following:. From your button action, call this method on sender.

    def print_view(sender): root=sender while root.superview: root=root.superview print('root:', root.name, type(root)) def print_subviews(v, prefix): print(prefix, v.name, type(v)) for sv in v.subviews: print_subviews(v, prefix+'+') print_subviews(root,'')

    That will print something like
    root UI.View
    +view1 UI.view
    ++button1 ui.Button
    ++textview1 UI.Textview
    +label1 UI.LabelView

    Etc. Hopefully this helps explain your view heirarchy.

    Note that often people create global variables, or attributes in the root view that point to deep subviews...

    root=ui.load_view (....) root.input_box=root['view1']['textview1'] ....

    Then you can simply refer to these in your callbacks functions without having to use superview/subview business in your callbacks. If your button takes action on a specific item, that ends up being cleaner. If instead you have 5 buttons using the same action, then you would use sender. Superview

  • Clearly I need to explore the API modules more :)

  • @low Import a .pyui file is also easy:
    Share it where you have it, f.i. in Apple Files app
    Run Pythonista script
    Import File

  • @JonB And there it is...and indeed pretty obvious, but I missed it.

    Thank you so much!

  • Those are custom attributes not custom arguments. what happens is the view is created, with no input args:


    then, arguments are set:

    v.width =.... v.bc="oooo"

    and so on.
    if you need to take action based on the custom attributes, you need to have those as @property's, to implement a setter.

  • @omz Thank you.

  • @Treenjood

    I wrote a script that converts the pyui attributes into text for copying into a py file as code. I will take a bit of manipulation to convert over some of the attributes, but it should help get you started. The code is available here.

  • try v['webview1'].load_url('https://google.com')

    or replace webview1 with whatever you named your webview in the designer.

  • @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.

  • @Webmaster4o , lol does not surprise me. But let's face it a lot of little treasures in Pythonista.
    I wish they could all be documented, but I understand why they are not. It would take a big chunk out of the development time. I can't remember how long I have had Pythonista now, but I am astounded how much it has evolved in that short time.

  • @ccc , sorry for not being clear. It goes into the pyui file. Screenshot, below. Sorry, I left it full screen so it's clear to others also.

    Edit: oops, that is creating a dynamic attr in pic. But it's the same place / same idea.

    The eval is called on the CustomAtrributes, in the ui.py function _view_from_dict

    Sorry a few edits: I rushed this. But the self.sayhello(1,2) evals() to calling a method of that name in our class. You can leave off the self and call a function, but I think from memory you have to return True, otherwise warnings are printed.
    This seems very close to what the action method does , when it's defined inside the pyui file.

  • Hmmm, I really think this is a nice update to what I posted here. I understand some may not think its good. Not sure.

    But by using the ViewWalker Class I posted the other day here

    And adding self.dict.update(ViewWalker(self).as_dict()) to the PYUIViewer class so you can access all your objects with dot notation rather than self['switch1'] for example.

    There is a limitation and that is your instance attrs and all your pyui objects have to have unique names. It's explained in the ViewWalker link. Hmmm, I am not sure it's really such a limitation. In a real app you would want this anyway.

    But if you load a pyui file in this manner and all your names are unique you have dot notation access to the objects as well as they are all accessible from the root level.

    I guess it would be nice to offer the same functionality (dot notation) in a hierarchical way instead of the flattened out way I am doing here.
    Hmmm, not sure I can figure that out just yet, @jonB, maybe that's something for you 😬😬
    Still nicer to do self.firstView.SecondView.ThirdView.switch1 = True
    Then ...
    self['firstView']...['switch1'].value = True

    I am not entirely sure why @omz didn't add this in somehow.

    Ok, I think it's exciting....maybe I am clapping with one hand 😱🎉🎉🎉🎉

    class PYUIViewer(ui.View): # this acts as a normal Custom ui.View class # the root view of the class is the pyui file read in def __init__(self, pyui_fn, *args, **kwargs): ui.load_view(pyui_fn, bindings={'MyClass': WrapInstance(self), 'self': self}) # call after so our kwargs modify attrs super().__init__(*args, **kwargs) # use the ViewWalker to get a dict of all the view controls/etc self.__dict__.update(ViewWalker(self).as_dict()) # i have a ui.Switch named switch1 in pyui file. # now i can refer to it directly, rather than use string # subscripts ie. self['switch1'] # but if could be self['v1']['sv1']['sv2']['switch1'] self['switch1'].value = False # conventional way self.switch1.value = False # after updating our dict
  • @omz, thanks. Nice to know. Is a nice feature. I know a small thing, but makes life easier

  • @JonB , I agree. I have been looking at ui.py
    But to be honest, I don't get it all. But as each day passes, I understand a little more each day. Maybe I will be dead before I get anywhere , but it's still a fun journey. But I am sure a lot of these quirks are things that omz did in his early development of the product, and he has to spread his time about what he focuses on.
    Personally, I find it hard to comprehend he has written Pythonista. He calls himself an indie programmer, well there needs to be more of them.
    I honestly can't wait to meet him. I am a smart guy, but in different ways. But I feel the same about you and ccc also. I can see you guys are very talented.
    Oh shit, I am a groupie :) I don't care. Love meeting and talking with exceptional people!

Internal error.

Oops! Looks like something went wrong!