Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
UI composites for the world
-
This recent thread by @cvp on how to right-align the label on a button prompted me to think about a more general solution to these kinds of requirements. Hence this utility view, available on GitHub.
Composite
is a custom Pythonista UI View that supports stacking several other views in the same area, while providing easy access to the features of the individual views.Example #1 - Basic use
Let's stack a ui.Label on top of a ui.Button:
lbl_btn = Composite(Button, Label)
Composite members are listed in bottom-up order, and you can provide both classes and instances; classes are simply instantiated with no arguments.
When you access or set an attribute on the composite, it is also applied in the order given to the constructor, above. Thus, if I set the alignment:
lbl_btn.alignment = ALIGN_RIGHT
This applies to the Label, as Button does not have that attribute. On the other hand, setting the background color:
lbl_btn.background_color = 'lightgrey'
Applies only to the first view, Button, even though the Label has that attribute as well.
The underlying Composite view grabs all attributes that affect the sizing and positioning of the whole composited view. For example:
lbl_btn.center = (100, 100)
Likewise, setting the size of the Composite will lay out all the contained views so that they all fill the whole area of the Composite.
If you need to explicitly manage a specific view, for example to manage which view gets touch events, you have two options:
- Instantiate it and retain the reference to it, in order to set values before or after giving it to the Composite constructor.
- Get the specific view by class name, and set specific values on it.
An example of the second option:
lbl_btn['Label'].border_color = 'black'
Example #2 - Margins and corners
Composite comes with a Margins view, which can be included to inset the following views by a set amount, by default 5 pixels from every edge:
lbl = Composite(Margins, Label)
You can set the margin values for every edge separately, with any of the following options:
- single number - Same amount on all sides
- 2-tuple - (top and bottom, sides)
- 3-tuple - (top, sides, bottom)
- 4-tuple - (top, right, bottom, left)
For example:
lbl.margin = (5, 10)
Sets top and bottom margins to 5 pixels, and side margins to 10 pixels.
Labels do not normally support rounded corners eithout some objc_util code, but luckily the containing View does, so this works as well:
lbl.corner_radius = 10
size_to_fit
also works as expected, arranging the whole Composite view around the preferred size of the innermost/topmost view.Example #3 - Shadow
Setting the shadow options for the whole Composite view is supported, either with the convenience function
set_drop_shadow
that only needs the color of the shadow as a parameter, or with the individual settings:- shadow_opacity
- shadow_offset (tuple)
- shadow_color
- shadow_radius
Example #4 - Blurred background
iOS BlurEffect is available through a separate auxiliary view:
blur_lbl = Composite(Blur, Button, Label)
style
option can be used to set the brightness of the blurred area in relation to the underlying layer to one of the following values:- LIGHTER
- SAME (default)
- DARKER
Run the examples
All of the above examples are demonstrated in the end of the
composite.py
file.To do
Editable views like TextField do not work right.
Should include the vibrancy effect as well.
Other composable examples are highly welcome.
Thanks to the following contributions on the Pythonista forum:
- ObjC code for the shadow settings
- BlurView code
-
Hi,
that does look quite nice. As already stated in the
ui
Thread, I am not a bigui
user, so this could be considered to be of most irrelevance. But I think the documentation is despite all efforts a bit lacking. I won't touch anything that does not provide at least method signatures for its constructor functions. Sounds a bit rude, but I hope you do understand how it is meant 😉.cheers
zipit -
You can automatically generate function/method signature docs: https://docs.python.org/3.6/library/pydoc.html
-
@ccc and all, I would sure appreciate any pointers to tools or tutorials that would help me generate a straight-forward GitHub-friendly markdown API doc out of my module, which is often just a single file.
-
On the Pythonista console:
>>> import composite >>> help(composite)
See how your docstrings appear in the generated docs? This should encourage you to write more docstrings on functions/methods that are not obvious. Always ask yourself if there are ways to structure / name your code to make docstrings unnecessary.
-
@ccc made a pull request which inspired me to think about how to make views even more easy to combine, customize and use.
As a result, the version now on Github allows me to define new components like this:
class FancyButton(Sized, Clickable, Rounded, Blurred, Shadowed, DefaultLabel): pass
... that can then be used wherever needed in the normal way of views:
btn = FancyButton(text='Click me')
... to get a sized-to-fit button with multiline centred text, rounded corners, blurred background and a drop shadow.
The above is of course a bit contrived example. In practice, I will first assemble my default look and feel (text color, rounded corners, background, maybe shadow, but probably not), and then apply it to labels and buttons and whatnot.
-
Couple of additional tweaks:
- Explicit but optional control for which view in the stack gets touch events.
- TextView tweak that lets you get out of editing mode by swiping down on the TextView (helps with the old problem of the keyboard on iPhone not having any option for getting out of the edit mode).
Could keep on building different kinds of components, but probably will not, unless someone has a specific thing in mind. Or until I get back to my UI-heavy project.