pythonenaml

How to make a custom container?


I want to make reusable Container with a title in the like in the banner example. (So it is like a custom GroupBox). So every element i add to the class should be added to the classes subcontainer, not to itself. See the code below, i marked the position where i just want to write the subelements.

from enaml.widgets.api import *
from enaml.layout.api import *
from enaml.styling import *

enamldef BannerSheet(StyleSheet):
    Style:
        element = 'Label'
        style_class = 'banner'
        Setter:
            field = 'background'
            value = ('lineargradient(x1: 0, y1:0, x2:0, y2:1, '
                        'stop: 0 #222222, stop: 0.5 #1A1A1A, stop: 1 #111111)')
        Setter:
            field = 'color'
            value = '#FFFFEF'
        Setter:
            field = 'padding'
            value = '5px'
        Setter:
            field = 'font'
            value = '18pt Verdana'

enamldef Banner(Container):
    BannerSheet:
        pass
    padding = 0
    alias title : lbl.text
    constraints = [lbl.left == left, 
                   lbl.right == right, 
                   con.top == lbl.bottom,
                   con.bottom<=bottom]
    Label: lbl:
        name = 'abd'   
        style_class = 'banner'

    Container: con:
        pass


enamldef DemoContainer(Container):
    padding = 0
    Banner: b:
        title = 'Bar'
        #i want children here to be put into the banner.con container.
        #so e.g.
        Label:
             text = 'This should be below the title label"

Solution

  • Enaml doesn't allow you to delegate the position of a widget in the hierarchy using the declarative syntax (you can do it with Include and procedural code). Your simplest option is to use a child Container on the banner, which holds the widgets. Something like the following:

    from enaml.widgets.api import *
    
    
    enamldef BannerContainer(Container):
        alias text: label.text
        Label: label:
            pass
    
    
    enamldef Main(Window):
        Container:
            BannerContainer:
                text = 'First Banner'
                Container:
                    padding = 0
                    Field: pass
                    Field: pass
                    Field: pass
            BannerContainer:
                text = 'Second Banner'
                Container:
                    padding = 0
                    PushButton: pass
                    PushButton: pass
                    PushButton: pass
    

    enter image description here

    Alternatively, you can subclass container and reimplement the layout_constraints method to do whatever you want for layout purposes (see the TaskDialog implementation for an example of this), or use templates and a compile-time for-each loop to expand the child widgets (see the advanced template example).