updated version to 20.0.1 fix demoapp_release_config fix demeapp_release_config (cherry-pick from e2647d8a317018b940da1390fd63f66461ac907b) Fix max sized of DivFrameLayout with match parent child (cherry-pick from 23fb83533a5e4aeb7f3e5ae1ad3831331bb2c7bc) Fix border drawing in animations (cherry-pick from 5c74b7c45d59566c6b0ffed5e007b9623f5e84a7) Cherry pickadd build config for demoapp release Check view is laid out before draw it to bitmap (cherry-pick from 8e610704677942792289bd1c0e58ddc0a0fd1887) Request layout after weight updating (cherry-pick from c6f488dd9b2c7f628de152672a73a95c98ce191d) added dns requirement into github task (cherry-pick from c6bcc5d6fdc7f1fa1ea2e09351c7a5ab622c4824) update changelog update changelog (cherry-pick from 7a50c5c8acc5288eb9123ef5e8d430d5148f0494) update api dump update div.api (cherry-pick from 0563c1635b4695e6a5c9cfa45d0be620056b2914) updated version to 20.0.0 (cherry-pick from 8894effabe2379433255ef389125c6bea46b74ae)
PyDIVKit examples
This library is designed to work with DivKit with python.
Features:
- Declarative and imperative DivKit blocks definition
- Native Type-hints support
- Complete object-oriented API
- IDE type checks and suggestions
Object construction
The main idea is to provide a tool for creating blocks using Python objects.
import json
import pydivkit as dk
container = dk.DivContainer(
items=[
dk.DivGallery(
items=[
dk.DivText(text="Hello from pydivkit")
]
)
]
)
print(json.dumps(container.dict(), indent=1))
# {
# "type": "container",
# "items": [
# {
# "type": "gallery",
# "items": [
# {
# "type": "text",
# "text": "Hello from pydivkit"
# }
# ]
# }
# ]
# }
Slider example
Following code is a rewritten slider example using pydivkit.
import pydivkit as dk
slider = dk.DivData(
log_id="sample_card",
states=[
dk.DivDataState(
state_id=0,
div=dk.DivSlider(
width=dk.DivMatchParentSize(),
max_value=10,
min_value=1,
thumb_style=dk.DivShapeDrawable(
color="#00b300",
stroke=dk.DivStroke(
color="#ffffff",
width=3,
),
shape=dk.DivRoundedRectangleShape(
item_width=dk.DivFixedSize(value=32),
item_height=dk.DivFixedSize(value=32),
corner_radius=dk.DivFixedSize(value=100)
),
),
track_active_style=dk.DivShapeDrawable(
color="#00b300",
shape=dk.DivRoundedRectangleShape(
item_height=dk.DivFixedSize(value=6)
)
),
track_inactive_style=dk.DivShapeDrawable(
color="#20000000",
shape=dk.DivRoundedRectangleShape(
item_height=dk.DivFixedSize(value=6)
)
)
)
)
]
)
This example might be serialised like this:
import json
print(json.dumps(slider.dict(), indent=1))
# {
# "log_id": "sample_card",
# "states": [
# {
# "div": {
# "type": "slider",
# "max_value": 10,
# "min_value": 1,
# "thumb_style": {
# "type": "shape_drawable",
# "color": "#00b300",
# "shape": {
# "type": "rounded_rectangle",
# "corner_radius": {
# "type": "fixed",
# "value": 100
# },
# "item_height": {
# "type": "fixed",
# "value": 32
# },
# "item_width": {
# "type": "fixed",
# "value": 32
# }
# },
# "stroke": {
# "color": "#ffffff",
# "width": 3
# }
# },
# "track_active_style": {
# "type": "shape_drawable",
# "color": "#00b300",
# "shape": {
# "type": "rounded_rectangle",
# "item_height": {
# "type": "fixed",
# "value": 6
# }
# }
# },
# "track_inactive_style": {
# "type": "shape_drawable",
# "color": "#20000000",
# "shape": {
# "type": "rounded_rectangle",
# "item_height": {
# "type": "fixed",
# "value": 6
# }
# }
# },
# "width": {
# "type": "match_parent"
# }
# },
# "state_id": 0
# }
# ]
# }
Templating and DRY
Of course, manually building blocks from your code every time is boring. So, the first idea is to move the initialization of DivKit objects into functions.
# Naive DRY example which strictly non-recommended
import pydivkit as dk
def get_size(value: int = 32) -> dk.DivFixedSize:
return dk.DivFixedSize(value=value)
def get_shape() -> dk.DivShape:
return dk.DivShape(
item_width=get_size(),
item_height=get_size(),
corner_radius=get_size(100)
)
slider_shape = get_shape()
slider = dk.DivData(
log_id="sample_card",
states=[
dk.DivDataState(
# other arguments
div=dk.DivSlider(
thumb_style=dk.DivShapeDrawable(
shape=slider_shape,
# other arguments
),
# other arguments
)
)
]
)
Looks a little better, but this approach doesn't scale well. To simplify layout and save traffic, DivKit has templates. This is a way to layout similar elements without having to declare the complete json, but just declare a template and use this many times in similar items.
PyDivKit supports defining templates through the inheritance.
Let's define an example card:
import json
import pydivkit as dk
class CategoriesItem(dk.DivContainer):
"""
Class inherited from dk.DivContainer will have a template
"""
# Special object for mark this fields a DivKit field in template
icon_url: str = dk.Field()
text: str = dk.Field()
# Set defaults layout for in the template
width = dk.DivWrapContentSize()
background = [dk.DivSolidBackground(color="#f0f0f0")]
content_alignment_vertical = dk.DivAlignmentVertical.CENTER
orientation = dk.DivContainerOrientation.HORIZONTAL
paddings = dk.DivEdgeInsets(left=12, right=12, top=10, bottom=10)
border = dk.DivBorder(corner_radius=12)
items = [
dk.DivImage(
width=dk.DivFixedSize(value=20),
height=dk.DivFixedSize(value=20),
margins=dk.DivEdgeInsets(right=6),
# Special object Ref it's a reference for Field property
image_url=dk.Ref(icon_url),
),
dk.DivText(
width=dk.DivWrapContentSize(),
max_lines=1,
# Special object Ref it's a reference for Field property
text=dk.Ref(text),
),
]
BASE_URL = "https://leonardo.edadeal.io/dyn/re/segments/level1/96"
# So after class definition you might use all the `Field` marked property
# names as an argument.
gallery = dk.DivGallery(
items=[
CategoriesItem(
text="Food", icon_url=f"{BASE_URL}/food.png",
),
CategoriesItem(
text="Alcohol", icon_url=f"{BASE_URL}/alcohol.png",
),
CategoriesItem(
text="Household", icon_url=f"{BASE_URL}/household.png",
),
]
)
print(json.dumps(dk.make_div(gallery), indent=1, ensure_ascii=False))
# {
# "templates": {
# "__main__.CategoriesItem": {
# "type": "container",
# "background": [
# {
# "type": "solid",
# "color": "#f0f0f0"
# }
# ],
# "border": {
# "corner_radius": 12
# },
# "content_alignment_vertical": "center",
# "items": [
# {
# "type": "image",
# "height": {
# "type": "fixed",
# "value": 20
# },
# "$image_url": "icon_url",
# "margins": {
# "right": 6
# },
# "width": {
# "type": "fixed",
# "value": 20
# }
# },
# {
# "type": "text",
# "max_lines": 1,
# "$text": "text",
# "width": {
# "type": "wrap_content"
# }
# }
# ],
# "orientation": "horizontal",
# "paddings": {
# "bottom": 10,
# "left": 12,
# "right": 12,
# "top": 10
# },
# "width": {
# "type": "wrap_content"
# }
# }
# },
# "card": {
# "log_id": "card",
# "states": [
# {
# "div": {
# "type": "gallery",
# "items": [
# {
# "type": "__main__.CategoriesItem",
# "icon_url": "https://leonardo.edadeal.io/dyn/re/segments/level1/96/food.png",
# "text": "Food"
# },
# {
# "type": "__main__.CategoriesItem",
# "icon_url": "https://leonardo.edadeal.io/dyn/re/segments/level1/96/alcohol.png",
# "text": "Alcohol"
# },
# {
# "type": "__main__.CategoriesItem",
# "icon_url": "https://leonardo.edadeal.io/dyn/re/segments/level1/96/household.png",
# "text": "Household"
# }
# ]
# },
# "state_id": 0
# }
# ]
# }
# }
Template names
By default, templates are collecting by the metaclass into shared storage when
the class is declaring at import time, and have the
format {module_name}.{class_name}.
The following example, sure will not occur in the wild, shows a warning if suddenly the names of the classes, and hence the templates, conflict.
import pydivkit as dk
class MyTemplate(dk.DivContainer):
width = dk.DivWrapContentSize()
class MyTemplate(dk.DivContainer):
pass
# RuntimeWarning: Template 'test.MyTemplate' already defined in
# <class 'test.MyTemplate'> and will be replaced to <class 'test.MyTemplate'>
Also, if you do not want to show the structure of your project to the outside,
or for some reason you need to make the example above clean, you can rename the
template by declaring a special attribute __template_name__
import pydivkit as dk
class MyTemplate(dk.DivContainer):
width = dk.DivWrapContentSize()
print(MyTemplate.template_name)
# >>> test.MyTemplate
class MyTemplate(dk.DivContainer):
__template_name__ = "MyTemplate2"
print(MyTemplate.template_name)
# >>> MyTemplate2