Updated kivymd

This commit is contained in:
Mark Qvist 2022-10-02 17:16:59 +02:00
parent c6df8b851b
commit abcf173cc8
160 changed files with 11617 additions and 6545 deletions

View File

@ -9,23 +9,16 @@ Is a collection of Material Design compliant widgets for use with,
a framework for cross-platform, touch-enabled graphical applications.
The project's goal is to approximate Google's `Material Design spec
<https://material.io/design/introduction>`_ as close as possible without
sacrificing ease of use or application performance.
sacrificing ease of use.
This library is a fork of the `KivyMD project
<https://gitlab.com/kivymd/KivyMD>`_ the author of which stopped supporting
this project three years ago. We found the strength and brought this project
to a new level. Currently we're in **beta** status, so things are changing
all the time and we cannot promise any kind of API stability.
However it is safe to vendor now and make use of what's currently available.
<https://gitlab.com/kivymd/KivyMD>`_. We found the strength and brought this
project to a new level.
Join the project! Just fork the project, branch out and submit a pull request
when your patch is ready. If any changes are necessary, we'll guide you
through the steps that need to be done via PR comments or access to your for
may be requested to outright submit them. If you wish to become a project
developer (permission to create branches on the project without forking for
easier collaboration), have at least one PR approved and ask for it.
If you contribute regularly to the project the role may be offered to you
without asking too.
If you wish to become a project developer (permission to create branches on the
project without forking for easier collaboration), have at least one PR
approved and ask for it. If you contribute regularly to the project the role
may be offered to you without asking too.
"""
import os
@ -33,7 +26,7 @@ import os
import kivy
from kivy.logger import Logger
__version__ = "1.0.0.dev0"
__version__ = "1.1.0.dev0"
"""KivyMD version."""
release = False
@ -56,6 +49,9 @@ images_path = os.path.join(path, f"images{os.sep}")
uix_path = os.path.join(path, "uix")
"""Path to uix directory."""
glsl_path = os.path.join(path, "data", "glsl")
"""Path to glsl directory."""
_log_message = (
"KivyMD:"
+ (" Release" if release else "")

View File

@ -1,5 +0,0 @@
# THIS FILE IS GENERATED FROM KIVYMD SETUP.PY
__version__ = '1.0.0.dev0'
__hash__ = '68ec8626a93b0e7f69e48d9755c4af70028f66a2'
__short_hash__ = '68ec862'
__date__ = '2022-07-07'

View File

@ -45,7 +45,7 @@ import os
from kivy.app import App
from kivy.lang import Builder
from kivy.logger import Logger
from kivy.properties import ObjectProperty
from kivy.properties import ObjectProperty, StringProperty
from kivymd.theming import ThemeManager
@ -71,6 +71,16 @@ class MDApp(App, FpsMonitoring):
information.
"""
icon = StringProperty("kivymd/images/logo/kivymd-icon-512.png")
"""
See :attr:`~kivy.app.App.icon` attribute for more information.
.. versionadded:: 1.1.0
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
adn default to `kivymd/images/logo/kivymd-icon-512.png`.
"""
theme_cls = ObjectProperty()
"""
Instance of :class:`~ThemeManager` class.

View File

@ -412,7 +412,7 @@ To demonstrate the shades of the palette, you can run the following code:
self.screen = Factory.Root()
for name_tab in colors.keys():
tab = Tab(text=name_tab)
tab = Tab(title=name_tab)
self.screen.ids.android_tabs.add_widget(tab)
return self.screen
@ -427,7 +427,7 @@ To demonstrate the shades of the palette, you can run the following code:
{
"viewclass": "ItemColor",
"md_bg_color": colors[tab_text][value_color],
"text": value_color,
"title": value_color,
}
)

View File

@ -0,0 +1,44 @@
/*
The shader code has been refactored for the KivyMD library.
You can find the original code of this shaders at the links:
https://www.shadertoy.com/view/WtdSDs
https://www.shadertoy.com/view/fsdyzB
Additional thanks to iq for optimizing conditional block for individual
corner radius:
https://iquilezles.org/articles/distfunctions
*/
float roundedBoxSDF(vec2 centerPosition, vec2 size, vec4 radius) {
radius.xy = (centerPosition.x > 0.0) ? radius.xy : radius.zw;
radius.x = (centerPosition.y > 0.0) ? radius.x : radius.y;
vec2 q = abs(centerPosition) - (size - shadow_softness) + radius.x;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius.x;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// Smooth the result (free antialiasing).
float edge0 = 0.0;
float smoothedAlpha = 1.0 - smoothstep(0.0, edge0, 1.0);
// Get the resultant shape.
vec4 quadColor = mix(
vec4(
shadow_color[0],
shadow_color[1],
shadow_color[2],
0.0
),
shadow_color,
smoothedAlpha
);
// Apply a drop shadow effect.
float shadowDistance = roundedBoxSDF(
fragCoord.xy - mouse.xy - (size / 2.0), size / 2.0, shadow_radius
);
float shadowAlpha = 1.0 - smoothstep(
-shadow_softness, shadow_softness, shadowDistance
);
fragColor = mix(quadColor, shadow_color, shadowAlpha - smoothedAlpha);
}

View File

@ -0,0 +1,10 @@
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 resolution;
uniform vec4 mouse;
uniform vec2 size;
uniform vec4 shadow_radius;
uniform float shadow_softness;
uniform vec4 shadow_color;

View File

@ -0,0 +1,10 @@
vec2 gfc(in vec4 fc) {
vec2 canvas_pos = resolution.zw;
vec2 uv = fc.xy;
uv.y -= canvas_pos.y;
return uv;
}
void main(void) {
mainImage(gl_FragColor, gfc(gl_FragCoord));
}

View File

@ -5,11 +5,15 @@ Register KivyMD widgets to use without import.
from kivy.factory import Factory
register = Factory.register
register("MDScrollView", module="kivymd.uix.scrollview")
register("MDRecycleView", module="kivymd.uix.recycleview")
register("MDResponsiveLayout", module="kivymd.uix.responsivelayout")
register("MDSegmentedControl", module="kivymd.uix.segmentedcontrol")
register("MDSegmentedControlItem", module="kivymd.uix.segmentedcontrol")
register("MDSliverAppbar", module="kivymd.uix.sliverappbar")
register("MDSliverAppbarContent", module="kivymd.uix.sliverappbar")
register("MDSliverAppbarHeader", module="kivymd.uix.sliverappbar")
register("MDNavigationRailItem", module="kivymd.uix.navigationrail")
register("MDNavigationRail", module="kivymd.uix.navigationrail")
register("MDNavigationRailFabButton", module="kivymd.uix.navigationrail")
register("MDNavigationRailMenuButton", module="kivymd.uix.navigationrail")
@ -29,7 +33,7 @@ register("MDExpansionPanel", module="kivymd.uix.expansionpanel")
register("MDExpansionPanelOneLine", module="kivymd.uix.expansionpanel")
register("MDExpansionPanelTwoLine", module="kivymd.uix.expansionpanel")
register("MDExpansionPanelThreeLine", module="kivymd.uix.expansionpanel")
register("FitImage", module="kivymd.utils.fitimage")
register("FitImage", module="kivymd.uix.fitimage")
register("MDBackdrop", module="kivymd.uix.backdrop")
register("MDBanner", module="kivymd.uix.banner")
register("MDTooltip", module="kivymd.uix.tooltip")
@ -54,10 +58,7 @@ register("MDCard", module="kivymd.uix.card")
register("MDSeparator", module="kivymd.uix.card")
register("MDSelectionList", module="kivymd.uix.selection")
register("MDChip", module="kivymd.uix.chip")
register("MDChooseChip", module="kivymd.uix.chip")
register("MDSmartTile", module="kivymd.uix.imagelist")
register("SmartTileWithLabel", module="kivymd.uix.imagelist")
register("SmartTileWithStar", module="kivymd.uix.imagelist")
register("MDLabel", module="kivymd.uix.label")
register("MDIcon", module="kivymd.uix.label")
register("MDList", module="kivymd.uix.list")
@ -65,7 +66,6 @@ register("ILeftBody", module="kivymd.uix.list")
register("ILeftBodyTouch", module="kivymd.uix.list")
register("IRightBody", module="kivymd.uix.list")
register("IRightBodyTouch", module="kivymd.uix.list")
register("ContainerSupport", module="kivymd.uix.list")
register("OneLineListItem", module="kivymd.uix.list")
register("TwoLineListItem", module="kivymd.uix.list")
register("ThreeLineListItem", module="kivymd.uix.list")
@ -99,9 +99,7 @@ register("MDSlider", module="kivymd.uix.slider")
register("MDSpinner", module="kivymd.uix.spinner")
register("MDTabs", module="kivymd.uix.tab")
register("MDTextField", module="kivymd.uix.textfield")
register("MDTextFieldRound", module="kivymd.uix.textfield")
register("MDTextFieldRect", module="kivymd.uix.textfield")
register("MDToolbar", module="kivymd.uix.toolbar")
register("MDTopAppBar", module="kivymd.uix.toolbar")
register("MDBottomAppBar", module="kivymd.uix.toolbar")
register("MDDropDownItem", module="kivymd.uix.dropdownitem")

View File

@ -12,7 +12,7 @@ Themes/Icon Definitions
List of icons from materialdesignicons.com. These expanded material design
icons are maintained by Austin Andrews (Templarian on Github).
LAST UPDATED: Version 6.9.96
LAST UPDATED: Version 7.0.96
To preview the icons and their names, you can use the following application:
----------------------------------------------------------------------------
@ -154,6 +154,8 @@ md_icons = {
"account-box-outline": "\U000F0007",
"account-cancel": "\U000F12DF",
"account-cancel-outline": "\U000F12E0",
"account-card": "\U000F1BA4",
"account-card-outline": "\U000F1BA5",
"account-cash": "\U000F1097",
"account-cash-outline": "\U000F1098",
"account-check": "\U000F0008",
@ -171,6 +173,8 @@ md_icons = {
"account-convert-outline": "\U000F1301",
"account-cowboy-hat": "\U000F0E9B",
"account-cowboy-hat-outline": "\U000F17F3",
"account-credit-card": "\U000F1BA6",
"account-credit-card-outline": "\U000F1BA7",
"account-details": "\U000F0631",
"account-details-outline": "\U000F1372",
"account-edit": "\U000F06BC",
@ -260,6 +264,7 @@ md_icons = {
"air-humidifier": "\U000F1099",
"air-humidifier-off": "\U000F1466",
"air-purifier": "\U000F0D44",
"air-purifier-off": "\U000F1B57",
"airbag": "\U000F0BE9",
"airballoon": "\U000F001C",
"airballoon-outline": "\U000F100B",
@ -480,7 +485,6 @@ md_icons = {
"amplifier-off": "\U000F11B5",
"anchor": "\U000F0031",
"android": "\U000F0032",
"android-messages": "\U000F0D45",
"android-studio": "\U000F0034",
"angle-acute": "\U000F0937",
"angle-obtuse": "\U000F0938",
@ -745,6 +749,7 @@ md_icons = {
"arrow-up-thin": "\U000F19B2",
"arrow-up-thin-circle-outline": "\U000F1597",
"arrow-vertical-lock": "\U000F115C",
"artboard": "\U000F1B9A",
"artstation": "\U000F0B5B",
"aspect-ratio": "\U000F0A24",
"assistant": "\U000F0064",
@ -762,6 +767,7 @@ md_icons = {
"attachment-off": "\U000F1AC3",
"attachment-plus": "\U000F1AC4",
"attachment-remove": "\U000F1AC5",
"atv": "\U000F1B70",
"audio-input-rca": "\U000F186B",
"audio-input-stereo-minijack": "\U000F186C",
"audio-input-xlr": "\U000F186D",
@ -774,6 +780,8 @@ md_icons = {
"autorenew": "\U000F006A",
"autorenew-off": "\U000F19E7",
"av-timer": "\U000F006B",
"awning": "\U000F1B87",
"awning-outline": "\U000F1B88",
"aws": "\U000F0E0F",
"axe": "\U000F08C8",
"axe-battle": "\U000F1842",
@ -987,6 +995,7 @@ md_icons = {
"beaker-remove": "\U000F1233",
"beaker-remove-outline": "\U000F1234",
"bed": "\U000F02E3",
"bed-clock": "\U000F1B94",
"bed-double": "\U000F0FD4",
"bed-double-outline": "\U000F0FD3",
"bed-empty": "\U000F08A0",
@ -1148,10 +1157,11 @@ md_icons = {
"book-sync": "\U000F169C",
"book-sync-outline": "\U000F16C8",
"book-variant": "\U000F00BF",
"book-variant-multiple": "\U000F00BC",
"bookmark": "\U000F00C0",
"bookmark-box": "\U000F1B75",
"bookmark-box-multiple": "\U000F196C",
"bookmark-box-multiple-outline": "\U000F196D",
"bookmark-box-outline": "\U000F1B76",
"bookmark-check": "\U000F00C1",
"bookmark-check-outline": "\U000F137B",
"bookmark-minus": "\U000F09CC",
@ -1339,8 +1349,11 @@ md_icons = {
"calendar-account": "\U000F0ED7",
"calendar-account-outline": "\U000F0ED8",
"calendar-alert": "\U000F0A31",
"calendar-alert-outline": "\U000F1B62",
"calendar-arrow-left": "\U000F1134",
"calendar-arrow-right": "\U000F1135",
"calendar-badge": "\U000F1B9D",
"calendar-badge-outline": "\U000F1B9E",
"calendar-blank": "\U000F00EE",
"calendar-blank-multiple": "\U000F1073",
"calendar-blank-outline": "\U000F0B66",
@ -1349,26 +1362,40 @@ md_icons = {
"calendar-clock": "\U000F00F0",
"calendar-clock-outline": "\U000F16E1",
"calendar-collapse-horizontal": "\U000F189D",
"calendar-collapse-horizontal-outline": "\U000F1B63",
"calendar-cursor": "\U000F157B",
"calendar-cursor-outline": "\U000F1B64",
"calendar-edit": "\U000F08A7",
"calendar-edit-outline": "\U000F1B65",
"calendar-end": "\U000F166C",
"calendar-end-outline": "\U000F1B66",
"calendar-expand-horizontal": "\U000F189E",
"calendar-expand-horizontal-outline": "\U000F1B67",
"calendar-export": "\U000F0B24",
"calendar-export-outline": "\U000F1B68",
"calendar-filter": "\U000F1A32",
"calendar-filter-outline": "\U000F1A33",
"calendar-heart": "\U000F09D2",
"calendar-heart-outline": "\U000F1B69",
"calendar-import": "\U000F0B25",
"calendar-import-outline": "\U000F1B6A",
"calendar-lock": "\U000F1641",
"calendar-lock-open": "\U000F1B5B",
"calendar-lock-open-outline": "\U000F1B5C",
"calendar-lock-outline": "\U000F1642",
"calendar-minus": "\U000F0D5C",
"calendar-minus-outline": "\U000F1B6B",
"calendar-month": "\U000F0E17",
"calendar-month-outline": "\U000F0E18",
"calendar-multiple": "\U000F00F1",
"calendar-multiple-check": "\U000F00F2",
"calendar-multiselect": "\U000F0A32",
"calendar-multiselect-outline": "\U000F1B55",
"calendar-outline": "\U000F0B67",
"calendar-plus": "\U000F00F3",
"calendar-plus-outline": "\U000F1B6C",
"calendar-question": "\U000F0692",
"calendar-question-outline": "\U000F1B6D",
"calendar-range": "\U000F0679",
"calendar-range-outline": "\U000F0B68",
"calendar-refresh": "\U000F01E1",
@ -1376,9 +1403,11 @@ md_icons = {
"calendar-remove": "\U000F00F4",
"calendar-remove-outline": "\U000F0C45",
"calendar-search": "\U000F094C",
"calendar-search-outline": "\U000F1B6E",
"calendar-star": "\U000F09D3",
"calendar-star-outline": "\U000F1B53",
"calendar-start": "\U000F166D",
"calendar-start-outline": "\U000F1B6F",
"calendar-sync": "\U000F0E8E",
"calendar-sync-outline": "\U000F0E8F",
"calendar-text": "\U000F00F5",
@ -1496,6 +1525,8 @@ md_icons = {
"car-outline": "\U000F14ED",
"car-parking-lights": "\U000F0D63",
"car-pickup": "\U000F07AA",
"car-search": "\U000F1B8D",
"car-search-outline": "\U000F1B8E",
"car-seat": "\U000F0FA4",
"car-seat-cooler": "\U000F0FA5",
"car-seat-heater": "\U000F0FA6",
@ -1584,6 +1615,7 @@ md_icons = {
"cart-minus": "\U000F0D68",
"cart-off": "\U000F066B",
"cart-outline": "\U000F0111",
"cart-percent": "\U000F1BAE",
"cart-plus": "\U000F0112",
"cart-remove": "\U000F0D69",
"cart-variant": "\U000F15EB",
@ -1969,6 +2001,7 @@ md_icons = {
"cloud-tags": "\U000F07B6",
"cloud-upload": "\U000F0167",
"cloud-upload-outline": "\U000F0B7E",
"clouds": "\U000F1B95",
"clover": "\U000F0816",
"coach-lamp": "\U000F1020",
"coach-lamp-variant": "\U000F1A37",
@ -2128,8 +2161,10 @@ md_icons = {
"contrast": "\U000F0195",
"contrast-box": "\U000F0196",
"contrast-circle": "\U000F0197",
"controller": "\U000F02B4",
"controller-classic": "\U000F0B82",
"controller-classic-outline": "\U000F0B83",
"controller-off": "\U000F02B5",
"cookie": "\U000F0198",
"cookie-alert": "\U000F16D0",
"cookie-alert-outline": "\U000F16D1",
@ -2278,6 +2313,7 @@ md_icons = {
"currency-sign": "\U000F07BE",
"currency-try": "\U000F01B2",
"currency-twd": "\U000F07BF",
"currency-uah": "\U000F1B9B",
"currency-usd": "\U000F01C1",
"currency-usd-off": "\U000F067A",
"current-ac": "\U000F1480",
@ -2391,8 +2427,6 @@ md_icons = {
"desk-lamp-on": "\U000F1B20",
"deskphone": "\U000F01C3",
"desktop-classic": "\U000F07C0",
"desktop-mac": "\U000F01C4",
"desktop-mac-dashboard": "\U000F09E8",
"desktop-tower": "\U000F01C5",
"desktop-tower-monitor": "\U000F0AAB",
"details": "\U000F01C6",
@ -2442,7 +2476,6 @@ md_icons = {
"disc": "\U000F05EE",
"disc-alert": "\U000F01D1",
"disc-player": "\U000F0960",
"discord": "\U000F066F",
"dishwasher": "\U000F0AAC",
"dishwasher-alert": "\U000F11B8",
"dishwasher-off": "\U000F11B9",
@ -2457,8 +2490,9 @@ md_icons = {
"diving": "\U000F1977",
"diving-flippers": "\U000F0DBF",
"diving-helmet": "\U000F0DC0",
"diving-scuba": "\U000F0DC1",
"diving-scuba": "\U000F1B77",
"diving-scuba-flag": "\U000F0DC2",
"diving-scuba-mask": "\U000F0DC1",
"diving-scuba-tank": "\U000F0DC3",
"diving-scuba-tank-multiple": "\U000F0DC4",
"diving-snorkel": "\U000F0DC5",
@ -2590,6 +2624,10 @@ md_icons = {
"email": "\U000F01EE",
"email-alert": "\U000F06CF",
"email-alert-outline": "\U000F0D42",
"email-arrow-left": "\U000F10DA",
"email-arrow-left-outline": "\U000F10DB",
"email-arrow-right": "\U000F10DC",
"email-arrow-right-outline": "\U000F10DD",
"email-box": "\U000F0D03",
"email-check": "\U000F0AB1",
"email-check-outline": "\U000F0AB2",
@ -2598,6 +2636,7 @@ md_icons = {
"email-fast": "\U000F186F",
"email-fast-outline": "\U000F1870",
"email-lock": "\U000F01F1",
"email-lock-outline": "\U000F1B61",
"email-mark-as-unread": "\U000F0B92",
"email-minus": "\U000F0EE5",
"email-minus-outline": "\U000F0EE6",
@ -2613,16 +2652,12 @@ md_icons = {
"email-outline": "\U000F01F0",
"email-plus": "\U000F09EB",
"email-plus-outline": "\U000F09EC",
"email-receive": "\U000F10DA",
"email-receive-outline": "\U000F10DB",
"email-remove": "\U000F1661",
"email-remove-outline": "\U000F1662",
"email-seal": "\U000F195B",
"email-seal-outline": "\U000F195C",
"email-search": "\U000F0961",
"email-search-outline": "\U000F0962",
"email-send": "\U000F10DC",
"email-send-outline": "\U000F10DD",
"email-sync": "\U000F12C7",
"email-sync-outline": "\U000F12C8",
"email-variant": "\U000F05F0",
@ -3013,7 +3048,15 @@ md_icons = {
"flag-remove-outline": "\U000F10B4",
"flag-triangle": "\U000F023F",
"flag-variant": "\U000F0240",
"flag-variant-minus": "\U000F1BB4",
"flag-variant-minus-outline": "\U000F1BB5",
"flag-variant-off": "\U000F1BB0",
"flag-variant-off-outline": "\U000F1BB1",
"flag-variant-outline": "\U000F023E",
"flag-variant-plus": "\U000F1BB2",
"flag-variant-plus-outline": "\U000F1BB3",
"flag-variant-remove": "\U000F1BB6",
"flag-variant-remove-outline": "\U000F1BB7",
"flare": "\U000F0D72",
"flash": "\U000F0241",
"flash-alert": "\U000F0EF7",
@ -3275,6 +3318,7 @@ md_icons = {
"format-list-checkbox": "\U000F096A",
"format-list-checks": "\U000F0756",
"format-list-group": "\U000F1860",
"format-list-group-plus": "\U000F1B56",
"format-list-numbered": "\U000F027B",
"format-list-numbered-rtl": "\U000F0D0D",
"format-list-text": "\U000F126F",
@ -3285,6 +3329,8 @@ md_icons = {
"format-paragraph": "\U000F027D",
"format-paragraph-spacing": "\U000F1AFD",
"format-pilcrow": "\U000F06D8",
"format-pilcrow-arrow-left": "\U000F0286",
"format-pilcrow-arrow-right": "\U000F0285",
"format-quote-close": "\U000F027E",
"format-quote-close-outline": "\U000F11A8",
"format-quote-open": "\U000F0757",
@ -3310,8 +3356,6 @@ md_icons = {
"format-text-wrapping-overflow": "\U000F0D0F",
"format-text-wrapping-wrap": "\U000F0D10",
"format-textbox": "\U000F0D11",
"format-textdirection-l-to-r": "\U000F0285",
"format-textdirection-r-to-l": "\U000F0286",
"format-title": "\U000F05F4",
"format-underline": "\U000F0287",
"format-underline-wavy": "\U000F18E9",
@ -3487,7 +3531,8 @@ md_icons = {
"glass-tulip": "\U000F02A8",
"glass-wine": "\U000F0876",
"glasses": "\U000F02AA",
"globe-light": "\U000F12D7",
"globe-light": "\U000F066F",
"globe-light-outline": "\U000F12D7",
"globe-model": "\U000F08E9",
"gmail": "\U000F02AB",
"gnome": "\U000F02AC",
@ -3512,15 +3557,12 @@ md_icons = {
"google-circles-group": "\U000F02B3",
"google-classroom": "\U000F02C0",
"google-cloud": "\U000F11F6",
"google-controller": "\U000F02B4",
"google-controller-off": "\U000F02B5",
"google-downasaur": "\U000F1362",
"google-drive": "\U000F02B6",
"google-earth": "\U000F02B7",
"google-fit": "\U000F096C",
"google-glass": "\U000F02B8",
"google-hangouts": "\U000F02C9",
"google-home": "\U000F0824",
"google-keep": "\U000F06DC",
"google-lens": "\U000F09F6",
"google-maps": "\U000F05F5",
@ -3582,6 +3624,7 @@ md_icons = {
"hand-clap-off": "\U000F1A42",
"hand-coin": "\U000F188F",
"hand-coin-outline": "\U000F1890",
"hand-cycle": "\U000F1B9C",
"hand-extended": "\U000F18B6",
"hand-extended-outline": "\U000F18B7",
"hand-front-left": "\U000F182B",
@ -3615,6 +3658,7 @@ md_icons = {
"harddisk-remove": "\U000F104C",
"hat-fedora": "\U000F0BA4",
"hazard-lights": "\U000F0C89",
"hdmi-port": "\U000F1BB8",
"hdr": "\U000F0D7D",
"hdr-off": "\U000F0D7E",
"head": "\U000F135E",
@ -3768,6 +3812,8 @@ md_icons = {
"home-roof": "\U000F112B",
"home-search": "\U000F13B0",
"home-search-outline": "\U000F13B1",
"home-silo": "\U000F1BA0",
"home-silo-outline": "\U000F1BA1",
"home-switch": "\U000F1794",
"home-switch-outline": "\U000F1795",
"home-thermometer": "\U000F0F54",
@ -3827,6 +3873,7 @@ md_icons = {
"human-pregnant": "\U000F05CF",
"human-queue": "\U000F1571",
"human-scooter": "\U000F11E9",
"human-walker": "\U000F1B71",
"human-wheelchair": "\U000F138D",
"human-white-cane": "\U000F1981",
"humble-bundle": "\U000F0744",
@ -4005,6 +4052,7 @@ md_icons = {
"klingon": "\U000F135B",
"knife": "\U000F09FB",
"knife-military": "\U000F09FC",
"knob": "\U000F1B96",
"koala": "\U000F173F",
"kodi": "\U000F0314",
"kubernetes": "\U000F10FE",
@ -4092,7 +4140,7 @@ md_icons = {
"leaf-off": "\U000F12D9",
"leak": "\U000F0DD7",
"leak-off": "\U000F0DD8",
"lecturn": "\U000F1AF0",
"lectern": "\U000F1AF0",
"led-off": "\U000F032B",
"led-on": "\U000F032C",
"led-outline": "\U000F032D",
@ -4182,6 +4230,8 @@ md_icons = {
"lipstick": "\U000F13B5",
"liquid-spot": "\U000F1826",
"liquor": "\U000F191E",
"list-box": "\U000F1B7B",
"list-box-outline": "\U000F1B7C",
"list-status": "\U000F15AB",
"litecoin": "\U000F0A61",
"loading": "\U000F0772",
@ -4362,6 +4412,8 @@ md_icons = {
"message-bookmark-outline": "\U000F15AD",
"message-bulleted": "\U000F06A2",
"message-bulleted-off": "\U000F06A3",
"message-check": "\U000F1B8A",
"message-check-outline": "\U000F1B8B",
"message-cog": "\U000F06F1",
"message-cog-outline": "\U000F1172",
"message-draw": "\U000F0363",
@ -4410,6 +4462,8 @@ md_icons = {
"metronome-tick": "\U000F07DB",
"micro-sd": "\U000F07DC",
"microphone": "\U000F036C",
"microphone-message": "\U000F050A",
"microphone-message-off": "\U000F050B",
"microphone-minus": "\U000F08B3",
"microphone-off": "\U000F036D",
"microphone-outline": "\U000F036E",
@ -4519,7 +4573,8 @@ md_icons = {
"more": "\U000F037B",
"mortar-pestle": "\U000F1748",
"mortar-pestle-plus": "\U000F03F1",
"mosque": "\U000F1827",
"mosque": "\U000F0D45",
"mosque-outline": "\U000F1827",
"mother-heart": "\U000F1314",
"mother-nurse": "\U000F0D21",
"motion": "\U000F15B2",
@ -4594,8 +4649,11 @@ md_icons = {
"movie-star-outline": "\U000F1718",
"mower": "\U000F166F",
"mower-bag": "\U000F1670",
"mower-bag-on": "\U000F1B60",
"mower-on": "\U000F1B5F",
"muffin": "\U000F098C",
"multicast": "\U000F1893",
"multimedia": "\U000F1B97",
"multiplication": "\U000F0382",
"multiplication-box": "\U000F0383",
"mushroom": "\U000F07DF",
@ -4624,6 +4682,7 @@ md_icons = {
"music-note-eighth-dotted": "\U000F0F71",
"music-note-half": "\U000F0389",
"music-note-half-dotted": "\U000F0F72",
"music-note-minus": "\U000F1B89",
"music-note-off": "\U000F038A",
"music-note-off-outline": "\U000F0F73",
"music-note-outline": "\U000F0F74",
@ -4846,7 +4905,13 @@ md_icons = {
"office-building-cog-outline": "\U000F194A",
"office-building-marker": "\U000F1520",
"office-building-marker-outline": "\U000F1521",
"office-building-minus": "\U000F1BAA",
"office-building-minus-outline": "\U000F1BAB",
"office-building-outline": "\U000F151F",
"office-building-plus": "\U000F1BA8",
"office-building-plus-outline": "\U000F1BA9",
"office-building-remove": "\U000F1BAC",
"office-building-remove-outline": "\U000F1BAD",
"oil": "\U000F03C7",
"oil-lamp": "\U000F0F19",
"oil-level": "\U000F1053",
@ -4967,6 +5032,8 @@ md_icons = {
"patio-heater": "\U000F0F80",
"patreon": "\U000F0882",
"pause": "\U000F03E4",
"pause-box": "\U000F00BC",
"pause-box-outline": "\U000F1B7A",
"pause-circle": "\U000F03E5",
"pause-circle-outline": "\U000F03E6",
"pause-octagon": "\U000F03E7",
@ -5253,6 +5320,7 @@ md_icons = {
"progress-clock": "\U000F0996",
"progress-close": "\U000F110A",
"progress-download": "\U000F0997",
"progress-helper": "\U000F1BA2",
"progress-pencil": "\U000F1787",
"progress-question": "\U000F1522",
"progress-star": "\U000F1788",
@ -5351,12 +5419,14 @@ md_icons = {
"razor-single-edge": "\U000F1998",
"react": "\U000F0708",
"read": "\U000F0447",
"receipt": "\U000F0449",
"receipt-outline": "\U000F19DC",
"receipt": "\U000F0824",
"receipt-outline": "\U000F04F7",
"receipt-text": "\U000F0449",
"receipt-text-check": "\U000F1A63",
"receipt-text-check-outline": "\U000F1A64",
"receipt-text-minus": "\U000F1A65",
"receipt-text-minus-outline": "\U000F1A66",
"receipt-text-outline": "\U000F19DC",
"receipt-text-plus": "\U000F1A67",
"receipt-text-plus-outline": "\U000F1A68",
"receipt-text-remove": "\U000F1A69",
@ -5495,7 +5565,9 @@ md_icons = {
"robot-off-outline": "\U000F167B",
"robot-outline": "\U000F167A",
"robot-vacuum": "\U000F070D",
"robot-vacuum-alert": "\U000F1B5D",
"robot-vacuum-variant": "\U000F0908",
"robot-vacuum-variant-alert": "\U000F1B5E",
"rocket": "\U000F0463",
"rocket-launch": "\U000F14DE",
"rocket-launch-outline": "\U000F14DF",
@ -5633,6 +5705,8 @@ md_icons = {
"segment": "\U000F0ECB",
"select": "\U000F0485",
"select-all": "\U000F0486",
"select-arrow-down": "\U000F1B59",
"select-arrow-up": "\U000F1B58",
"select-color": "\U000F0D31",
"select-compare": "\U000F0AD9",
"select-drag": "\U000F0A6C",
@ -5803,6 +5877,7 @@ md_icons = {
"sign-pole": "\U000F14F8",
"sign-real-estate": "\U000F1118",
"sign-text": "\U000F0782",
"sign-yield": "\U000F1BAF",
"signal": "\U000F04A2",
"signal-2g": "\U000F0712",
"signal-3g": "\U000F0713",
@ -5821,7 +5896,8 @@ md_icons = {
"signature-freehand": "\U000F0DFC",
"signature-image": "\U000F0DFD",
"signature-text": "\U000F0DFE",
"silo": "\U000F0B49",
"silo": "\U000F1B9F",
"silo-outline": "\U000F0B49",
"silverware": "\U000F04A3",
"silverware-clean": "\U000F0FDE",
"silverware-fork": "\U000F04A4",
@ -6000,6 +6076,9 @@ md_icons = {
"speaker-message": "\U000F1B11",
"speaker-multiple": "\U000F0D38",
"speaker-off": "\U000F04C4",
"speaker-pause": "\U000F1B73",
"speaker-play": "\U000F1B72",
"speaker-stop": "\U000F1B74",
"speaker-wireless": "\U000F071F",
"spear": "\U000F1845",
"speedometer": "\U000F04C5",
@ -6149,7 +6228,17 @@ md_icons = {
"store-settings": "\U000F18D4",
"store-settings-outline": "\U000F18D5",
"storefront": "\U000F07C7",
"storefront-check": "\U000F1B7D",
"storefront-check-outline": "\U000F1B7E",
"storefront-edit": "\U000F1B7F",
"storefront-edit-outline": "\U000F1B80",
"storefront-minus": "\U000F1B83",
"storefront-minus-outline": "\U000F1B84",
"storefront-outline": "\U000F10C1",
"storefront-plus": "\U000F1B81",
"storefront-plus-outline": "\U000F1B82",
"storefront-remove": "\U000F1B85",
"storefront-remove-outline": "\U000F1B86",
"stove": "\U000F04DE",
"strategy": "\U000F11D6",
"stretch-to-page": "\U000F0F2B",
@ -6239,6 +6328,7 @@ md_icons = {
"table-edit": "\U000F04F0",
"table-eye": "\U000F1094",
"table-eye-off": "\U000F13C3",
"table-filter": "\U000F1B8C",
"table-furniture": "\U000F05BC",
"table-headers-eye": "\U000F121D",
"table-headers-eye-off": "\U000F121E",
@ -6272,7 +6362,6 @@ md_icons = {
"table-sync": "\U000F13A1",
"table-tennis": "\U000F0E68",
"tablet": "\U000F04F6",
"tablet-android": "\U000F04F7",
"tablet-cellphone": "\U000F09A7",
"tablet-dashboard": "\U000F0ECE",
"taco": "\U000F0762",
@ -6377,8 +6466,6 @@ md_icons = {
"text-search-variant": "\U000F1A7E",
"text-shadow": "\U000F0669",
"text-short": "\U000F09A9",
"text-to-speech": "\U000F050A",
"text-to-speech-off": "\U000F050B",
"texture": "\U000F050C",
"texture-box": "\U000F0FE6",
"theater": "\U000F050D",
@ -6429,13 +6516,13 @@ md_icons = {
"timeline-check-outline": "\U000F1533",
"timeline-clock": "\U000F11FB",
"timeline-clock-outline": "\U000F11FC",
"timeline-help": "\U000F0F99",
"timeline-help-outline": "\U000F0F9A",
"timeline-minus": "\U000F1534",
"timeline-minus-outline": "\U000F1535",
"timeline-outline": "\U000F0BD2",
"timeline-plus": "\U000F0F96",
"timeline-plus-outline": "\U000F0F97",
"timeline-question": "\U000F0F99",
"timeline-question-outline": "\U000F0F9A",
"timeline-remove": "\U000F1536",
"timeline-remove-outline": "\U000F1537",
"timeline-text": "\U000F0BD3",
@ -6601,6 +6688,7 @@ md_icons = {
"transition-masked": "\U000F0916",
"translate": "\U000F05CA",
"translate-off": "\U000F0E06",
"translate-variant": "\U000F1B99",
"transmission-tower": "\U000F0D3E",
"transmission-tower-export": "\U000F192C",
"transmission-tower-import": "\U000F192D",
@ -6744,7 +6832,11 @@ md_icons = {
"vector-intersection": "\U000F055D",
"vector-line": "\U000F055E",
"vector-link": "\U000F0FE8",
"vector-point": "\U000F055F",
"vector-point": "\U000F01C4",
"vector-point-edit": "\U000F09E8",
"vector-point-minus": "\U000F1B78",
"vector-point-plus": "\U000F1B79",
"vector-point-select": "\U000F055F",
"vector-polygon": "\U000F0560",
"vector-polygon-variant": "\U000F1856",
"vector-polyline": "\U000F0561",
@ -6912,6 +7004,7 @@ md_icons = {
"water-alert-outline": "\U000F1503",
"water-boiler": "\U000F0F92",
"water-boiler-alert": "\U000F11B3",
"water-boiler-auto": "\U000F1B98",
"water-boiler-off": "\U000F11B4",
"water-check": "\U000F1504",
"water-check-outline": "\U000F1505",
@ -6951,6 +7044,7 @@ md_icons = {
"weather-cloudy-alert": "\U000F0F2F",
"weather-cloudy-arrow-right": "\U000F0E6E",
"weather-cloudy-clock": "\U000F18F6",
"weather-dust": "\U000F1B5A",
"weather-fog": "\U000F0591",
"weather-hail": "\U000F0592",
"weather-hazy": "\U000F0F30",
@ -7072,6 +7166,7 @@ md_icons = {
"window-restore": "\U000F05B2",
"window-shutter": "\U000F111C",
"window-shutter-alert": "\U000F111D",
"window-shutter-auto": "\U000F1BA3",
"window-shutter-cog": "\U000F1A8A",
"window-shutter-open": "\U000F111E",
"window-shutter-settings": "\U000F1A8B",
@ -7084,7 +7179,12 @@ md_icons = {
"wrap": "\U000F05B6",
"wrap-disabled": "\U000F0BDF",
"wrench": "\U000F05B7",
"wrench-check": "\U000F1B8F",
"wrench-check-outline": "\U000F1B90",
"wrench-clock": "\U000F19A3",
"wrench-clock-outline": "\U000F1B93",
"wrench-cog": "\U000F1B91",
"wrench-cog-outline": "\U000F1B92",
"wrench-outline": "\U000F0BE0",
"xamarin": "\U000F0845",
"xml": "\U000F05C0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1 +0,0 @@
{"quad_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "quad_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "quad_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1 +0,0 @@
{"rec_shadow-1.png": {"20": [2, 266, 256, 128], "21": [260, 266, 256, 128], "22": [518, 266, 256, 128], "23": [776, 266, 256, 128], "3": [260, 136, 256, 128], "2": [2, 136, 256, 128], "5": [776, 136, 256, 128], "4": [518, 136, 256, 128], "7": [260, 6, 256, 128], "6": [2, 6, 256, 128], "9": [776, 6, 256, 128], "8": [518, 6, 256, 128]}, "rec_shadow-0.png": {"11": [518, 266, 256, 128], "10": [260, 266, 256, 128], "13": [2, 136, 256, 128], "12": [776, 266, 256, 128], "15": [518, 136, 256, 128], "14": [260, 136, 256, 128], "17": [2, 6, 256, 128], "16": [776, 136, 256, 128], "19": [518, 6, 256, 128], "18": [260, 6, 256, 128], "1": [776, 6, 256, 128], "0": [2, 266, 256, 128]}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1 +0,0 @@
{"rec_st_shadow-0.png": {"11": [262, 138, 128, 256], "10": [132, 138, 128, 256], "13": [522, 138, 128, 256], "12": [392, 138, 128, 256], "15": [782, 138, 128, 256], "14": [652, 138, 128, 256], "16": [912, 138, 128, 256], "0": [2, 138, 128, 256]}, "rec_st_shadow-1.png": {"20": [522, 138, 128, 256], "21": [652, 138, 128, 256], "17": [2, 138, 128, 256], "23": [912, 138, 128, 256], "19": [262, 138, 128, 256], "18": [132, 138, 128, 256], "22": [782, 138, 128, 256], "1": [392, 138, 128, 256]}, "rec_st_shadow-2.png": {"3": [132, 138, 128, 256], "2": [2, 138, 128, 256], "5": [392, 138, 128, 256], "4": [262, 138, 128, 256], "7": [652, 138, 128, 256], "6": [522, 138, 128, 256], "9": [912, 138, 128, 256], "8": [782, 138, 128, 256]}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1 +0,0 @@
{"round_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "round_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "round_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}

View File

@ -0,0 +1,9 @@
from kivy.tests.common import GraphicUnitTest
from kivymd.app import MDApp
class BaseTest(GraphicUnitTest):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.app = MDApp() # NOQA

View File

@ -35,8 +35,15 @@ assert "Icons" in LabelBase._fonts.keys() # NOQA
images = os.listdir(kivymd.images_path)
print(images)
assert "logo" in images
assert "alpha_layer.png" in images
assert "black.png" in images
assert "blue.png" in images
assert "red.png" in images
assert "green.png" in images
assert "yellow.png" in images
assert "folder.png" in images
assert "rec_shadow.atlas" in images
assert "transparent.png" in images
"""
)
pyi_main.run(

View File

@ -0,0 +1,24 @@
from kivymd.tests.base_test import BaseTest
class BackdropTest(BaseTest):
def test_backdrop_raw_app(self):
from kivymd.uix.backdrop import MDBackdrop
from kivymd.uix.backdrop.backdrop import (
MDBackdropBackLayer,
MDBackdropFrontLayer,
)
from kivymd.uix.screen import MDScreen
from kivymd.uix.widget import MDWidget
self.render(
MDScreen(
MDBackdrop(
MDBackdropBackLayer(MDWidget()),
MDBackdropFrontLayer(MDWidget()),
id="backdrop",
title="Example Backdrop",
header_text="Menu:",
)
)
)

View File

@ -0,0 +1,32 @@
from kivymd.tests.base_test import BaseTest
class BottomNavigationTest(BaseTest):
def test_bottom_navigation_m3_style_raw_app(self):
from kivymd.uix.bottomnavigation import (
MDBottomNavigation,
MDBottomNavigationItem,
)
from kivymd.uix.screen import MDScreen
self.app.theme_cls.material_style = "M3"
self.render(
MDScreen(
MDBottomNavigation(
MDBottomNavigationItem(
name="screen 1",
text="Mail",
icon="gmail",
),
MDBottomNavigationItem(
name="screen 2",
text="Twitter",
icon="twitter",
badge_icon="numeric-10",
),
panel_color="#eeeaea",
selected_color_background="#97ecf8",
text_color_active="red",
)
)
)

View File

@ -0,0 +1,25 @@
from kivymd.tests.base_test import BaseTest
class CardTest(BaseTest):
def test_card_m3_style_raw_app(self):
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
from kivymd.uix.screen import MDScreen
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
pass
self.app.theme_cls.material_style = "M3"
self.render(
MDScreen(
MD3Card(
size_hint=(None, None),
pos_hint={"center_x": 0.5, "center_y": 0.5},
size=("200dp", "100dp"),
line_color=(0.2, 0.2, 0.2, 0.8),
style="elevated",
md_bg_color="lightblue",
)
)
)

View File

@ -0,0 +1,16 @@
from kivymd.tests.base_test import BaseTest
class ChipTest(BaseTest):
def test_chip_raw_app(self):
from kivymd.uix.chip import MDChip
from kivymd.uix.screen import MDScreen
self.render(
MDScreen(
MDChip(
text="Portland",
pos_hint={"center_x": 0.5, "center_y": 0.5},
)
)
)

View File

@ -1,14 +1,13 @@
def test_create_project():
import os
import sys
os.system(
f"{sys.executable} -m kivymd.tools.patterns.create_project "
f"python3.10 -m kivymd.tools.patterns.create_project "
f"MVC "
f"{os.path.expanduser('~')} "
f"TestProject "
f"{sys.executable} "
f"master "
f"python3.10 "
f"stable "
f"--name_screen TestProjectScreen "
f"--name_database restdb "
f"--use_hotreload yes"

View File

@ -0,0 +1,24 @@
from kivymd.tests.base_test import BaseTest
class FitImageTest(BaseTest):
def test_fitimage_raw_app(self):
import os
from kivymd import images_path
from kivymd.uix.fitimage import FitImage
from kivymd.uix.screen import MDScreen
self.render(
MDScreen(
FitImage(
source=os.path.join(
images_path, "logo", "kivymd-icon-512.png"
),
size_hint=(0.5, 0.5),
pos_hint={"center_x": 0.5, "center_y": 0.5},
radius=[36, 36, 0, 0],
mipmap=True,
)
)
)

View File

@ -0,0 +1,39 @@
from kivymd.tests.base_test import BaseTest
class ImageListTest(BaseTest):
def test_imagelist_raw_app(self):
import os
from kivymd import images_path
from kivymd.uix.button import MDIconButton
from kivymd.uix.imagelist import MDSmartTile
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
self.render(
MDScreen(
MDSmartTile(
MDIconButton(
icon="heart-outline",
theme_icon_color="Custom",
icon_color="red",
pos_hint={"center_y": 0.5},
),
MDLabel(
text="Julia and Julie",
bold=True,
color="white",
),
radius=24,
box_radius=[0, 0, 24, 24],
box_color="grey",
source=os.path.join(
images_path, "logo", "kivymd-icon-512.png"
),
pos_hint={"center_x": 0.5, "center_y": 0.5},
size_hint=(None, None),
size=("320dp", "320dp"),
)
)
)

View File

@ -0,0 +1,67 @@
from kivymd.tests.base_test import BaseTest
class ListTest(BaseTest):
def test_list_raw_app(self):
import os
from kivymd import images_path
from kivymd.uix.list import (
IconLeftWidget,
IconRightWidget,
ImageLeftWidget,
IRightBodyTouch,
MDList,
OneLineAvatarIconListItem,
OneLineAvatarListItem,
OneLineIconListItem,
OneLineListItem,
ThreeLineListItem,
TwoLineListItem,
)
from kivymd.uix.screen import MDScreen
from kivymd.uix.scrollview import MDScrollView
from kivymd.uix.selectioncontrol import MDCheckbox
class RightCheckbox(IRightBodyTouch, MDCheckbox):
pass
self.render(
MDScreen(
MDScrollView(
MDList(
OneLineListItem(text="Text"),
TwoLineListItem(
text="Text", secondary_text="secondary text"
),
ThreeLineListItem(
text="Text",
secondary_text="secondary text",
tertiary_text="tertiary text",
),
OneLineAvatarListItem(
ImageLeftWidget(
source=os.path.join(
images_path, "logo", "kivymd-icon-512.png"
)
),
text="Text",
),
OneLineIconListItem(
IconLeftWidget(icon="plus"),
text="Text",
),
OneLineAvatarIconListItem(
IconLeftWidget(icon="plus"),
IconRightWidget(icon="minus"),
text="Text",
),
OneLineAvatarIconListItem(
IconLeftWidget(icon="plus"),
RightCheckbox(),
text="Text",
),
)
)
)
)

View File

@ -0,0 +1,94 @@
from kivymd.tests.base_test import BaseTest
class NavigationDrawerTest(BaseTest):
def test_navigationdrawer_raw_app(self):
from kivymd.uix.navigationdrawer import (
MDNavigationDrawer,
MDNavigationDrawerDivider,
MDNavigationDrawerHeader,
MDNavigationDrawerItem,
MDNavigationDrawerLabel,
MDNavigationDrawerMenu,
MDNavigationLayout,
)
from kivymd.uix.screen import MDScreen
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.uix.toolbar import MDTopAppBar
class DrawerClickableItem(MDNavigationDrawerItem):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.focus_color = "#e7e4c0"
self.unfocus_color = "#f7f4e7"
self.text_color = "#4a4939"
self.icon_color = "#4a4939"
self.ripple_color = "#c5bdd2"
self.selected_color = "#0c6c4d"
class DrawerLabelItem(MDNavigationDrawerItem):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.bg_color = "#f7f4e7"
self.text_color = "#4a4939"
self.icon_color = "#4a4939"
_no_ripple_effect = True # NOQA
self.app.theme_cls.material_style = "M3"
self.render(
MDNavigationLayout(
MDScreenManager(
MDScreen(
MDTopAppBar(
title="Navigation Drawer",
elevation=10,
pos_hint={"top": 1},
md_bg_color="#e7e4c0",
specific_text_color="#4a4939",
left_action_items=[
["menu", lambda x: self.nav_drawer_open()]
],
)
)
),
MDNavigationDrawer(
MDNavigationDrawerMenu(
MDNavigationDrawerHeader(
title="Header title",
title_color="#4a4939",
text="Header text",
spacing="4dp",
padding=("12dp", 0, 0, "56dp"),
),
MDNavigationDrawerLabel(
text="Mail",
),
DrawerClickableItem(
icon="gmail",
right_text="+99",
text_right_color="#4a4939",
text="Inbox",
radius=24,
),
DrawerClickableItem(
icon="send",
text="Outbox",
radius=24,
),
MDNavigationDrawerDivider(),
MDNavigationDrawerLabel(
text="Labels",
),
DrawerLabelItem(
icon="information-outline",
text="Label",
),
DrawerLabelItem(
icon="information-outline",
text="Label",
),
),
id="nav_drawer",
),
)
)

View File

@ -0,0 +1,14 @@
from kivymd.tests.base_test import BaseTest
class TabTest(BaseTest):
def test_tab_raw_app(self):
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.tab import MDTabs, MDTabsBase
class Tab(MDFloatLayout, MDTabsBase):
pass
tab = MDTabs()
tab.add_widget(Tab(title="Tab"))
self.render(tab)

View File

@ -0,0 +1,72 @@
# from kivy.clock import Clock
# from kivy.uix.textinput import TextInput
from kivymd.tests.base_test import BaseTest
class TextFieldTest(BaseTest):
def test_textfield_raw_app(self):
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.screen import MDScreen
from kivymd.uix.textfield import MDTextField
# def set_text():
# for widget in self.screen.ids.box.children:
# if issubclass(widget.__class__, TextInput):
# widget.text = "Input text"
self.render(
MDScreen(
MDBoxLayout(
MDTextField(
hint_text="Label",
helper_text="Error massage",
mode="rectangle",
max_text_length=5,
),
MDTextField(
icon_left="git",
hint_text="Label",
helper_text="Error massage",
mode="rectangle",
),
MDTextField(
icon_left="git",
hint_text="Label",
helper_text="Error massage",
mode="fill",
),
MDTextField(
hint_text="Label",
helper_text="Error massage",
mode="fill",
),
MDTextField(
hint_text="Label",
helper_text="Error massage",
),
MDTextField(
icon_left="git",
hint_text="Label",
helper_text="Error massage",
),
MDTextField(
hint_text="Round mode",
mode="round",
max_text_length=15,
helper_text="Massage",
),
MDFlatButton(
text="SET TEXT",
pos_hint={"center_x": 0.5},
),
id="box",
orientation="vertical",
spacing="20dp",
adaptive_height=True,
size_hint_x=0.8,
pos_hint={"center_x": 0.5, "center_y": 0.5},
)
)
)

File diff suppressed because it is too large Load Diff

View File

@ -298,8 +298,9 @@ class MDApp(BaseApp):
from kivy.core.window import Window
from kivy.utils import get_color_from_hex
Window.clearcolor = get_color_from_hex("#e50000")
scroll = Factory.ScrollView(scroll_y=0)
scroll = Factory.MDScrollView(
scroll_y=0, md_bg_color=get_color_from_hex("#e50000")
)
lbl = Factory.Label(
text_size=(Window.width - 100, None),
size_hint_y=None,

View File

@ -13,6 +13,18 @@ from pathlib import Path
import kivymd
datas = [
# Add `.frag` files from the `kivymd/data/glsl/elevation` directory.
(
str(Path(kivymd.glsl_path).joinpath("elevation")) + os.sep,
str(
Path("kivymd").joinpath(
str(Path(kivymd.glsl_path)).split(str(Path("kivymd")) + os.sep)[
1
]
+ f"{os.sep}elevation"
)
),
),
# Add `.ttf` files from the `kivymd/fonts` directory.
(
kivymd.fonts_path,

View File

@ -1,3 +0,0 @@
%s
def get_view(self) -> %s:
return self.view

View File

@ -1,26 +0,0 @@
# FILE TO FIND AND CREATE LOCALIZATION FILES FOR YOUR APPLICATION. \
\
In this file, you can specify in which files of your project to search for \
localization strings. \
These files should be listed in the below command: \
\
\
xgettext -Lpython --output=messages.pot --from-code=utf-8 \
path/to/file-1 \
path/to/file-2 \
...
.PHONY: po mo
po:
xgettext -Lpython --output=messages.pot --from-code=utf-8 \
View/%s/%s.kv \
View/%s/%s.py
msgmerge --update --no-fuzzy-matching --backup=off data/locales/po/en.po messages.pot
msgmerge --update --no-fuzzy-matching --backup=off data/locales/po/ru.po messages.pot
mo:
mkdir -p data/locales/en/LC_MESSAGES
mkdir -p data/locales/ru/LC_MESSAGES
msgfmt -c -o data/locales/en/LC_MESSAGES/%s.mo data/locales/po/en.po
msgfmt -c -o data/locales/ru/LC_MESSAGES/%s.mo data/locales/po/ru.po

View File

@ -1,33 +0,0 @@
# The model implements the observer pattern. This means that the class must
# support adding, removing, and alerting observers. In this case, the model is
# completely independent of controllers and views. It is important that all
# registered observers implement a specific method that will be called by the
# model when they are notified (in this case, it is the `model_is_changed`
# method). For this, observers must be descendants of an abstract class,
# inheriting which, the `model_is_changed` method must be overridden.
class BaseScreenModel:
"""Implements a base class for model modules."""
_observers = []
def add_observer(self, observer) -> None:
self._observers.append(observer)
def remove_observer(self, observer) -> None:
self._observers.remove(observer)
def notify_observers(self, name_screen: str) -> None:
"""
Method that will be called by the observer when the model data changes.
:param name_screen:
name of the view for which the method should be called
:meth:`model_is_changed`.
"""
for observer in self._observers:
if observer.name == name_screen:
observer.model_is_changed()
break

View File

@ -1,16 +0,0 @@
# Of course, "very flexible Python" allows you to do without an abstract
# superclass at all or use the clever exception `NotImplementedError`. In my
# opinion, this can negatively affect the architecture of the application.
# I would like to point out that using Kivy, one could use the on-signaling
# model. In this case, when the state changes, the model will send a signal
# that can be received by all attached observers. This approach seems less
# universal - you may want to use a different library in the future.
class Observer:
"""Abstract superclass for all observers."""
def model_is_changed(self):
"""
The method that will be called on the observer when the model changes.
"""

View File

@ -1,72 +0,0 @@
#:import images_path kivymd.images_path
#:import colors kivymd.color_definitions.colors
#:import get_color_from_hex kivy.utils.get_color_from_hex
<%s>
FitImage:
source:
( \
f"{images_path}restdb-logo.png" \
if root.model.database.name == "RestDB" else \
f"{images_path}firebase-logo.png" \
) \
if hasattr(root.model, "database") else \
f"{images_path}transparent.png"
MDBoxLayout:
orientation: "vertical"
MDToolbar:
id: toolbar
title: "%s"
right_action_items: [["web", lambda x: %s]]
md_bg_color:
( \
get_color_from_hex(colors["Yellow"]["700"]) \
if root.model.database.name == "Firebase" else \
get_color_from_hex(colors["Blue"]["300"]) \
) \
if hasattr(root.model, "database") else \
app.theme_cls.primary_color
MDFloatLayout:
MDBoxLayout:
orientation: "vertical"
adaptive_height: True
size_hint_x: None
width: root.width - dp(72)
radius: 12
padding: "12dp"
md_bg_color: 1, 1, 1, .5
pos_hint: {"center_x": .5, "center_y": .5}
MDLabel:
id: prev_label
text: %s
font_style: "H6"
adaptive_height: True
halign: "center"
color: 1, 1, 1, 1
MDBoxLayout:
orientation: "vertical"
adaptive_height: True
padding: "50dp"
spacing: "20dp"
MDTextField:
hint_text: %s
on_text: root.controller.set_user_data("login", self.text)
MDTextField:
hint_text: %s
on_text: root.controller.set_user_data("password", self.text)
MDFillRoundFlatButton:
text: %s
on_release: root.controller.on_tap_button_login()
pos_hint: {"center_x": .5, "center_y": .1}
md_bg_color: toolbar.md_bg_color

View File

@ -1,15 +0,0 @@
%s
from View.base_screen import BaseScreenView
class %s(BaseScreenView):
"""Implements the login start screen in the user application."""
%s
def model_is_changed(self) -> None:
"""
Called whenever any change has occurred in the data model.
The view in this method tracks these changes and updates the UI
according to these changes.
"""
%s

View File

@ -1,47 +0,0 @@
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from kivymd.theming import ThemableBehavior
from kivymd.uix.screen import MDScreen
from Utility.observer import Observer
class BaseScreenView(ThemableBehavior, MDScreen, Observer):
"""
A base class that implements a visual representation of the model data
:class:`~Model.%s.%s`.
The view class must be inherited from this class.
"""
controller = ObjectProperty()
"""
Controller object - :class:`~Controller.%s.%s`.
:attr:`controller` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
model = ObjectProperty()
"""
Model object - :class:`~Model.%s.%s`.
:attr:`model` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
manager_screens = ObjectProperty()
"""
Screen manager object - :class:`~kivy.uix.screenmanager.ScreenManager`.
:attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `None`.
"""
def __init__(self, **kw):
super().__init__(**kw)
# Often you need to get access to the application object from the view
# class. You can do this using this attribute.
self.app = MDApp.get_running_app()
# Adding a view class as observer.
self.model.add_observer(self)

View File

@ -1,13 +0,0 @@
# The screens dictionary contains the objects of the models and controllers
# of the screens of the application.
from Model.%s import %s
from Controller.%s import %s
screens = {
%s: {
"model": %s,
"controller": %s,
},
}

View File

@ -1,54 +0,0 @@
"""
The entry point to the application.
The application uses the MVC template. Adhering to the principles of clean
architecture means ensuring that your application is easy to test, maintain,
and modernize.
You can read more about this template at the links below:
https://github.com/HeaTTheatR/LoginAppMVC
https://en.wikipedia.org/wiki/Modelviewcontroller
"""
from typing import NoReturn
from kivy.uix.screenmanager import ScreenManager%s
from kivymd.app import MDApp
from View.screens import screens%s
%s
class %s(MDApp):%s
def __init__(self, **kwargs):
super().__init__(**kwargs)%s
self.load_all_kv_files(self.directory)
# This is the screen manager that will contain all the screens of your
# application.
self.manager_screens = ScreenManager()
%s
def build(self) -> ScreenManager:
self.generate_application_screens()
return self.manager_screens
def generate_application_screens(self) -> NoReturn:
"""
Creating and adding screens to the screen manager.
You should not change this cycle unnecessarily. He is self-sufficient.
If you need to add any screen, open the `View.screens.py` module and
see how new screens are added according to the given application
architecture.
"""
for i, name_screen in enumerate(screens.keys()):
model = screens[name_screen]["model"](%s)
controller = screens[name_screen]["controller"](model)
view = controller.get_view()
view.manager_screens = self.manager_screens
view.name = name_screen
self.manager_screens.add_widget(view)
%s%s
%s().run()

View File

@ -0,0 +1,210 @@
"""
The script creates a new View package
=====================================
The script creates a new View package in an existing project with an MVC
template created using the create_project utility.
.. versionadded:: 1.0.0
.. seealso::
`Utility create_project <https://kivymd.readthedocs.io/en/latest/api/kivymd/tools/patterns/create_project/>`_
.. rubric:: Use a clean architecture for your applications.
To add a new view to an existing project that was created using the
`create_project` utility, use the following command::
kivymd.add_view \\
name_pattern \\
path_to_project \\
name_view
Example command::
kivymd.add_view \\
MVC \\
/Users/macbookair/Projects \\
NewScreen
You can also add new views with responsive behavior to an existing project::
kivymd.add_view \\
MVC \\
/Users/macbookair/Projects \\
NewScreen \\
--use_responsive yes
For more information about adaptive design,
`see here <https://kivymd.readthedocs.io/en/latest/components/responsivelayout/>`_.
"""
__all__ = [
"main",
]
import os
import re
from kivy import Logger
from kivymd.tools.argument_parser import ArgumentParserWithHelp
from kivymd.tools.patterns.create_project import (
chek_camel_case_name_project,
create_common_responsive_module,
create_controller,
create_model,
create_view,
)
screens_data = """%s
screens = {%s
}"""
screns_comment = """# The screen's dictionary contains the objects of the models and controllers
# of the screens of the application.
"""
def main():
"""The function of adding a new view to the project."""
global screens_data
parser = create_argument_parser()
args = parser.parse_args()
# pattern_name isn't used currently, will be used if new patterns is added in future
pattern_name = args.pattern # noqa F841
path_to_project = args.directory
name_view = args.name
use_responsive = args.use_responsive
if not os.path.exists(path_to_project):
parser.error(f"Project <{path_to_project}> does not exist...")
if name_view[-6:] != "Screen":
parser.error(
f"The name of the <{name_view}> screen should contain the word "
f"'Screen' at the end.\n"
"For example - '--name_screen MyFirstScreen ...'"
)
if name_view in os.listdir(os.path.join(path_to_project, "View")):
parser.error(
f"The <{name_view}> view also exists in the <{path_to_project}> project..."
)
# Create model.
name_database = (
"yes"
if "database.py" in os.listdir(os.path.join(path_to_project, "Model"))
else "no"
)
module_name = chek_camel_case_name_project(name_view)
if not module_name:
parser.error(
"The name of the screen should be written in camel case style. "
"\nFor example - 'MyFirstScreen'"
)
module_name = "_".join([name.lower() for name in module_name])
path_to_project = path_to_project
create_model(name_view, module_name, name_database, path_to_project)
# Create controller.
# FIXME: This is not a very good solution in order to understand whether
# a project uses a hot reload or not. Because the string
# 'from kivymd.tools.hotreload.app import MDApp' in the project can just
# be commented out and the project does not actually use hot reload.
with open(os.path.join(path_to_project, "main.py")) as main_module:
if "from kivymd.tools.hotreload.app import MDApp" in main_module.read():
use_hotreload = "yes"
else:
use_hotreload = "no"
create_controller(
name_view, module_name, use_hotreload, path_to_project
)
# Create View.
if use_responsive == "no":
create_view(name_view, module_name, [], path_to_project)
else:
create_view(name_view, module_name, [name_view], path_to_project)
create_common_responsive_module([name_view], path_to_project)
# Create 'View.screens.py module'.
create_screens_data(name_view, module_name, path_to_project)
Logger.info(
f"KivyMD: The {name_view} view has been added to the project..."
)
def create_screens_data(
name_view: str, module_name: str, path_to_project: str
) -> None:
with open(
os.path.join(path_to_project, "View", "screens.py")
) as screen_module:
screen_module = screen_module.read()
imports = re.findall(
"from Model.*Model|from Controller.*Controller", screen_module
)
screens = ""
path_to_view = os.path.join(path_to_project, "View")
for name in os.listdir(path_to_view):
if os.path.isdir(os.path.join(path_to_view, name)):
res = re.findall("[A-Z][a-z]*", name)
if res and len(res) == 2 and res[-1] == "Screen":
screens += (
"\n '%s': {"
"\n 'model': %s,"
"\n 'controller': %s,"
"\n },"
% (
f"{res[0].lower()} {res[1].lower()}",
f'{"".join(res)}Model',
f'{"".join(res)}Controller',
)
)
imports.append(f"from Model.{module_name} import {name_view}Model")
imports.append(
f"from Controller.{module_name} import {name_view}Controller"
)
imports.insert(0, screns_comment)
screens = screens_data % ("\n".join(imports), screens)
with open(
os.path.join(path_to_project, "View", "screens.py"), "w"
) as screen_module:
screen_module.write(screens)
def create_argument_parser() -> ArgumentParserWithHelp:
parser = ArgumentParserWithHelp(
prog="create_project.py",
allow_abbrev=False,
)
parser.add_argument(
"pattern",
help="the name of the pattern with which the project will be created.",
)
parser.add_argument(
"directory",
help="the directory of the project to which you want to add a new view.",
)
parser.add_argument(
"name",
help="the name of the view to add to an existing project.",
)
parser.add_argument(
"--use_responsive",
default="no",
help="whether to create a view with responsive behavior.",
)
return parser
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2021 Artem Bulgakov
# Copyright (c) 2019-2022 Artem Bulgakov
#
# This file is distributed under the terms of the same license,
# as the Kivy framework.

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2021 Artem Bulgakov
# Copyright (c) 2019-2022 Artem Bulgakov
#
# This file is distributed under the terms of the same license,
# as the Kivy framework.

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019-2021 Artem Bulgakov
# Copyright (c) 2019-2022 Artem Bulgakov
#
# This file is distributed under the terms of the same license,
# as the Kivy framework.

View File

@ -10,7 +10,7 @@ with some widget properties. For example:
AnchorLayout
------------
.. code-block::
.. code-block:: kv
AnchorLayout:
canvas:
@ -20,65 +20,13 @@ AnchorLayout
pos: self.pos
size: self.size
AnchorLayout
------------
.. code-block::
MDBoxLayout:
md_bg_color: app.theme_cls.primary_color
Available options are:
----------------------
- adaptive_height_
- adaptive_width_
- adaptive_size_
.. adaptive_height:
adaptive_height
---------------
.. code-block:: kv
adaptive_height: True
Equivalent
.. code-block:: kv
size_hint_y: None
height: self.minimum_height
.. adaptive_width:
adaptive_width
MDAnchorLayout
--------------
.. code-block:: kv
adaptive_width: True
Equivalent
.. code-block:: kv
size_hint_x: None
height: self.minimum_width
.. adaptive_size:
adaptive_size
-------------
.. code-block:: kv
adaptive_size: True
Equivalent
.. code-block:: kv
size_hint: None, None
size: self.minimum_size
MDAnchorLayout:
md_bg_color: app.theme_cls.primary_color
"""
__all__ = ("MDAnchorLayout",)
@ -86,7 +34,11 @@ __all__ = ("MDAnchorLayout",)
from kivy.uix.anchorlayout import AnchorLayout
from kivymd.uix import MDAdaptiveWidget
from kivymd.uix.behaviors import DeclarativeBehavior
class MDAnchorLayout(AnchorLayout, MDAdaptiveWidget):
pass
class MDAnchorLayout(DeclarativeBehavior, AnchorLayout, MDAdaptiveWidget):
"""
Anchor layout class. For more information, see in the
:class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation.
"""

View File

@ -35,8 +35,8 @@
if not root.front_layer_color \
else root.front_layer_color
radius:
[root.radius_left, root.radius_left,
root.radius_right, root.radius_right]
[root.radius_left, root.radius_right,
0, 0]
OneLineListItem:
id: header_button

View File

@ -31,6 +31,10 @@ Usage
Example
-------
.. tabs::
.. tab:: Declarative KV styles
.. code-block:: python
from kivy.lang import Builder
@ -41,8 +45,10 @@ Example
# Your layouts.
Builder.load_string(
'''
#:import os os
#:import Window kivy.core.window.Window
#:import IconLeftWidget kivymd.uix.list.IconLeftWidget
#:import images_path kivymd.images_path
<ItemBackdropFrontLayer@TwoLineAvatarListItem>
@ -64,7 +70,7 @@ Example
<MyBackdropBackLayer@Image>
size_hint: .8, .8
source: "data/logo/kivy-icon-512.png"
source: os.path.join(images_path, "logo", "kivymd-icon-512.png")
pos_hint: {"center_x": .5, "center_y": .6}
'''
)
@ -97,18 +103,75 @@ Example
pass
class TestBackdrop(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return ExampleBackdrop()
TestBackdrop().run()
Example().run()
.. tab:: Declarative python styles
.. code-block:: python
import os
from kivy.core.window import Window
from kivy.uix.image import Image
from kivymd import images_path
from kivymd.uix.backdrop import MDBackdrop
from kivymd.uix.backdrop.backdrop import (
MDBackdropBackLayer, MDBackdropFrontLayer
)
from kivymd.uix.list import TwoLineAvatarListItem, IconLeftWidget
from kivymd.uix.screen import MDScreen
from kivymd.app import MDApp
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDBackdrop(
MDBackdropBackLayer(
Image(
size_hint=(0.8, 0.8),
source=os.path.join(images_path, "logo", "kivymd-icon-512.png"),
pos_hint={"center_x": 0.5, "center_y": 0.6},
)
),
MDBackdropFrontLayer(
TwoLineAvatarListItem(
IconLeftWidget(icon="transfer-down"),
text="Lower the front layer",
secondary_text=" by 50 %",
on_press=self.backdrop_open_by_50_percent,
pos_hint={"top": 1},
_no_ripple_effect=True,
),
),
id="backdrop",
title="Example Backdrop",
radius_left="25dp",
radius_right="0dp",
header_text="Menu:",
)
)
)
def backdrop_open_by_50_percent(self, *args):
self.root.ids.backdrop.open(-Window.height / 2)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop.gif
:width: 280 px
:align: center
.. Note:: `See full example <https://github.com/kivymd/KivyMD/wiki/Components-Backdrop>`_
@ -139,7 +202,7 @@ from kivy.uix.boxlayout import BoxLayout
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.card import MDCard
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.toolbar.toolbar import ActionTopAppBarButton, MDTopAppBar
@ -151,7 +214,7 @@ with open(
Builder.load_string(kv_file.read())
class MDBackdrop(ThemableBehavior, MDFloatLayout):
class MDBackdrop(MDFloatLayout, ThemableBehavior):
"""
:Events:
:attr:`on_open`
@ -167,6 +230,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
.. versionadded:: 1.0.0
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-anchor-title.png
:align: center
:attr:`anchor_title` is an :class:`~kivy.properties.OptionProperty`
and defaults to `'left'`.
"""
@ -175,6 +241,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
"""
Padding for contents of the front layer.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-padding.png
:align: center
:attr:`padding` is an :class:`~kivy.properties.ListProperty`
and defaults to `[0, 0, 0, 0]`.
"""
@ -210,6 +279,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
"""
Background color of back layer.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-back-layer-color.png
:align: center
:attr:`back_layer_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
@ -218,6 +290,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
"""
Background color of front layer.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-front-layer-color.png
:align: center
:attr:`front_layer_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
@ -227,6 +302,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
The value of the rounding radius of the upper left corner
of the front layer.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-radius-left.png
:align: center
:attr:`radius_left` is an :class:`~kivy.properties.NumericProperty`
and defaults to `16dp`.
"""
@ -244,6 +322,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
"""
Whether to use a header above the contents of the front layer.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-header.png
:align: center
:attr:`header` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `True`.
"""
@ -252,6 +333,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
"""
Text of header.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-header-text.png
:align: center
:attr:`header_text` is an :class:`~kivy.properties.StringProperty`
and defaults to `'Header'`.
"""
@ -261,6 +345,9 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
The name of the icon that will be installed on the toolbar
on the left when opening the front layer.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/backdrop-close-icon.png
:align: center
:attr:`close_icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'close'`.
"""
@ -310,8 +397,8 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
_open_icon = ""
_front_layer_open = False
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.register_event_type("on_open")
self.register_event_type("on_close")
Clock.schedule_once(
@ -332,9 +419,12 @@ class MDBackdrop(ThemableBehavior, MDFloatLayout):
self._open_icon = self.left_action_items[0][0]
def on_header(self, instance_backdrop, value: bool) -> None:
def on_header(*args):
if not value:
self.ids._front_layer.remove_widget(self.ids.header_button)
Clock.schedule_once(on_header)
def open(self, open_up_to: int = 0) -> None:
"""
Opens the front layer.
@ -425,11 +515,11 @@ class MDBackdropToolbar(MDTopAppBar):
"""Implements a toolbar for back content."""
class MDBackdropFrontLayer(BoxLayout):
class MDBackdropFrontLayer(MDBoxLayout):
"""Container for front content."""
class MDBackdropBackLayer(BoxLayout):
class MDBackdropBackLayer(MDBoxLayout):
"""Container for back content."""
@ -437,5 +527,5 @@ class _BackLayer(BoxLayout):
pass
class _FrontLayer(MDCard, FakeRectangularElevationBehavior):
class _FrontLayer(MDCard):
pass

View File

@ -35,7 +35,7 @@ Usage
MDTopAppBar:
id: toolbar
title: "Example Banners"
elevation: 10
elevation: 4
pos_hint: {'top': 1}
MDBoxLayout:
@ -157,7 +157,6 @@ from kivy.properties import (
from kivy.uix.widget import Widget
from kivymd import uix_path
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.card import MDCard
@ -177,7 +176,7 @@ with open(
Builder.load_string(kv_file.read())
class MDBanner(MDCard, FakeRectangularElevationBehavior):
class MDBanner(MDCard):
vertical_pad = NumericProperty(dp(68))
"""
Indent the banner at the top of the screen.

View File

@ -5,22 +5,26 @@ Behaviors
Modules and classes implementing various behaviors for buttons etc.
"""
# flake8: NOQA
from .hover_behavior import HoverBehavior # isort:skip
from .backgroundcolor_behavior import (
BackgroundColorBehavior,
SpecificBackgroundColorBehavior,
)
# flake8: NOQA
from .declarative_behavior import DeclarativeBehavior
from .elevation import (
CircularElevationBehavior,
CommonElevationBehavior,
FakeCircularElevationBehavior,
FakeRectangularElevationBehavior,
ObservableShadow,
RectangularElevationBehavior,
RoundedRectangularElevationBehavior,
)
from .focus_behavior import FocusBehavior
from .magic_behavior import MagicBehavior
from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior
from .rotate_behavior import RotateBehavior
from .scale_behavior import ScaleBehavior
from .stencil_behavior import StencilBehavior
from .touch_behavior import TouchBehavior
from .hover_behavior import HoverBehavior # isort:skip

View File

@ -7,8 +7,9 @@ Behaviors/Background Color
__all__ = ("BackgroundColorBehavior", "SpecificBackgroundColorBehavior")
from typing import List
from typing import List, Union
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.properties import (
ColorProperty,
@ -24,8 +25,6 @@ from kivy.utils import get_color_from_hex
from kivymd.color_definitions import hue, palette, text_colors
from kivymd.theming import ThemeManager
from .elevation import CommonElevationBehavior
Builder.load_string(
"""
#:import RelativeLayout kivy.uix.relativelayout.RelativeLayout
@ -38,7 +37,7 @@ Builder.load_string(
angle: self.angle
origin: self._background_origin
Color:
rgba: self.md_bg_color
rgba: self._md_bg_color
RoundedRectangle:
group: "Background_instruction"
size: self.size
@ -67,7 +66,7 @@ Builder.load_string(
)
class BackgroundColorBehavior(CommonElevationBehavior):
class BackgroundColorBehavior:
background = StringProperty()
"""
Background image path.
@ -153,15 +152,26 @@ class BackgroundColorBehavior(CommonElevationBehavior):
_background_x = NumericProperty(0)
_background_y = NumericProperty(0)
_background_origin = ReferenceListProperty(
_background_x,
_background_y,
)
_background_origin = ReferenceListProperty(_background_x, _background_y)
_md_bg_color = ColorProperty([0, 0, 0, 0])
def __init__(self, **kwarg):
super().__init__(**kwarg)
self.bind(pos=self.update_background_origin)
def on_md_bg_color(self, instance_md_widget, color: Union[list, str]):
if (
hasattr(self, "theme_cls")
and self.theme_cls.theme_style_switch_animation
):
Animation(
_md_bg_color=color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self._md_bg_color = color
def update_background_origin(
self, instance_md_widget, pos: List[float]
) -> None:
@ -206,12 +216,14 @@ class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
super().__init__(**kwargs)
if hasattr(self, "theme_cls"):
self.theme_cls.bind(
primary_palette=self._update_specific_text_color
primary_palette=self._update_specific_text_color,
accent_palette=self._update_specific_text_color,
theme_style=self._update_specific_text_color,
)
self.bind(
background_hue=self._update_specific_text_color,
background_palette=self._update_specific_text_color,
)
self.theme_cls.bind(accent_palette=self._update_specific_text_color)
self.theme_cls.bind(theme_style=self._update_specific_text_color)
self.bind(background_hue=self._update_specific_text_color)
self.bind(background_palette=self._update_specific_text_color)
self._update_specific_text_color(None, None)
def _update_specific_text_color(
@ -234,5 +246,17 @@ class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
secondary_color[3] = 0.54
else:
secondary_color[3] = 0.7
if (
hasattr(self, "theme_cls")
and self.theme_cls.theme_style_switch_animation
):
Animation(
specific_text_color=color,
specific_secondary_text_color=secondary_color,
d=self.theme_cls.theme_style_switch_animation_duration,
t="linear",
).start(self)
else:
self.specific_text_color = color
self.specific_secondary_text_color = secondary_color

View File

@ -0,0 +1,317 @@
"""
Behaviors/Declarative
=====================
.. versionadded:: 1.0.0
.. raw:: html
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; height: auto;">
<iframe
src="https://www.youtube.com/embed/_kiaJacLz8o"
frameborder="0"
allowfullscreen
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">
</iframe>
</div>
As you already know, the Kivy framework provides the best/simplest/modern
UI creation tool that allows you to separate the logic of your application
from the description of the properties of widgets/GUI components.
This tool is named `KV Language <https://kivy.org/doc/stable/guide/lang.html>`_.
But in addition to creating a user interface using the KV Language Kivy allows
you to create user interface elements directly in the Python code.
And if you've ever created a user interface in Python code, you know how ugly
it looks. Even in the simplest user interface design, which was created using
Python code it is impossible to trace the widget tree, because in Python code
you build the user interface in an imperative style.
Imperative style
----------------
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
class Example(MDApp):
def build(self):
screen = MDScreen()
bottom_navigation = MDBottomNavigation(
panel_color="#eeeaea",
selected_color_background="#97ecf8",
text_color_active="white",
)
data = {
"screen 1": {"text": "Mail", "icon": "gmail"},
"screen 2": {"text": "Discord", "icon": "discord"},
"screen 3": {"text": "LinkedIN", "icon": "linkedin"},
}
for key in data.keys():
text = data[key]["text"]
navigation_item = MDBottomNavigationItem(
name=key, text=text, icon=data[key]["icon"]
)
navigation_item.add_widget(MDLabel(text=text, halign="center"))
bottom_navigation.add_widget(navigation_item)
screen.add_widget(bottom_navigation)
return screen
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-styles-programming.png
:align: center
Take a look at the above code example. This is a very simple UI. But looking
at this code, you will not be able to figure the widget tree and understand
which UI this code implements. This is named imperative programming style,
which is used in Kivy.
Now let's see how the same code is implemented using the KV language,
which uses a declarative style of describing widget properties.
Declarative style with KV language
----------------------------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
class Test(MDApp):
def build(self):
return Builder.load_string(
'''
MDScreen:
MDBottomNavigation:
panel_color: "#eeeaea"
selected_color_background: "#97ecf8"
text_color_active: "white"
MDBottomNavigationItem:
name: "screen 1"
text: "Mail"
icon: "gmail"
MDLabel:
text: "Mail"
halign: "center"
MDBottomNavigationItem:
name: "screen 2"
text: "Discord"
icon: "discord"
MDLabel:
text: "Discord"
halign: "center"
MDBottomNavigationItem:
name: "screen 3"
text: "LinkedIN"
icon: "linkedin"
MDLabel:
text: "LinkedIN"
halign: "center"
'''
)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation-styles-programming.png
:align: center
Looking at this code, we can now clearly see the widget tree and their properties.
We can quickly navigate through the components of the screen and quickly
change/add new properties/widgets. This is named declarative UI creation style.
But now the KivyMD library allows you to write Python code in a declarative style.
Just as it is implemented in Flutter/Jetpack Compose/SwiftUI.
Declarative style with Python code
----------------------------------
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
class Example(MDApp):
def build(self):
return (
MDScreen(
MDBottomNavigation(
MDBottomNavigationItem(
MDLabel(
text="Mail",
halign="center",
),
name="screen 1",
text="Mail",
icon="gmail",
),
MDBottomNavigationItem(
MDLabel(
text="Discord",
halign="center",
),
name="screen 2",
text="Discord",
icon="discord",
),
MDBottomNavigationItem(
MDLabel(
text="LinkedIN",
halign="center",
),
name="screen 3",
text="LinkedIN",
icon="linkedin",
),
panel_color="#eeeaea",
selected_color_background="#97ecf8",
text_color_active="white",
)
)
)
Example().run()
.. note:: The KivyMD library does not support creating Kivy widgets in Python
code in a declarative style.
But you can still use the declarative style of creating Kivy widgets in Python code.
To do this, you need to create a new class that will be inherited from the Kivy
widget and the :class:`~DeclarativeBehavior` class:
.. code-block:: python
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivymd.app import MDApp
from kivymd.uix.behaviors import DeclarativeBehavior
class DeclarativeStyleBoxLayout(DeclarativeBehavior, BoxLayout):
pass
class Example(MDApp):
def build(self):
return (
DeclarativeStyleBoxLayout(
Button(),
Button(),
orientation="vertical",
)
)
Example().run()
Get objects by identifiers
--------------------------
In the declarative style in Python code, the ids parameter of the specified
widget will return only the id of the child widget/container, ignoring other ids.
Therefore, to get objects by identifiers in declarative style in Python code,
you must specify all the container ids in which the widget is nested until you
get to the desired id:
.. code-block::
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.floatlayout import MDFloatLayout
class Example(MDApp):
def build(self):
return (
MDBoxLayout(
MDFloatLayout(
MDRaisedButton(
id="button_1",
text="Button 1",
pos_hint={"center_x": 0.5, "center_y": 0.5},
),
id="box_container_1",
),
MDBoxLayout(
MDFloatLayout(
MDRaisedButton(
id="button_2",
text="Button 2",
pos_hint={"center_x": 0.5, "center_y": 0.5},
),
id="float_container",
),
id="box_container_2",
)
)
)
def on_start(self):
# {
# 'box_container_1': <kivymd.uix.floatlayout.MDFloatLayout>,
# 'box_container_2': <kivymd.uix.boxlayout.MDBoxLayout object>
# }
print(self.root.ids)
# <kivymd.uix.button.button.MDRaisedButton>
print(self.root.ids.box_container_2.ids.float_container.ids.button_2)
Example().run()
Yes, this is not a very good solution, but I think it will be fixed soon.
.. warning:: Declarative programming style in Python code in the KivyMD library
is an experimental feature. Therefore, if you receive errors, do not hesitate
to create new issue in the KivyMD repository.
"""
from kivy.properties import StringProperty
from kivy.uix.widget import Widget
class DeclarativeBehavior:
"""
Implements the creation and addition of child widgets as declarative
programming style.
"""
id = StringProperty()
"""
Widget ID.
:attr:`id` is an :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
def __init__(self, *args, **kwargs):
super().__init__(**kwargs)
for child in args:
if issubclass(child.__class__, Widget):
self.add_widget(child)
if hasattr(child, "id") and child.id:
self.ids[child.id] = child

File diff suppressed because it is too large Load Diff

View File

@ -100,23 +100,35 @@ class FocusBehavior(HoverBehavior, ButtonBehavior):
def on_enter(self):
"""Called when mouse enter the bbox of the widget."""
if hasattr(self, "md_bg_color") and self.focus_behavior:
if (
hasattr(self, "md_bg_color") or hasattr(self, "bg_color")
) and self.focus_behavior:
if hasattr(self, "theme_cls") and not self.focus_color:
self.md_bg_color = self.theme_cls.bg_normal
color = self.theme_cls.bg_normal
else:
if not self.focus_color:
self.md_bg_color = App.get_running_app().theme_cls.bg_normal
color = App.get_running_app().theme_cls.bg_normal
else:
self.md_bg_color = self.focus_color
color = self.focus_color
self._set_bg_color(color)
def on_leave(self):
"""Called when the mouse exit the widget."""
if hasattr(self, "md_bg_color") and self.focus_behavior:
if (
hasattr(self, "md_bg_color") or hasattr(self, "bg_color")
) and self.focus_behavior:
if hasattr(self, "theme_cls") and not self.unfocus_color:
self.md_bg_color = self.theme_cls.bg_light
color = self.theme_cls.bg_light
else:
if not self.unfocus_color:
self.md_bg_color = App.get_running_app().theme_cls.bg_light
color = App.get_running_app().theme_cls.bg_light
else:
self.md_bg_color = self.unfocus_color
color = self.unfocus_color
self._set_bg_color(color)
def _set_bg_color(self, color):
if hasattr(self, "md_bg_color"):
self.md_bg_color = color
elif hasattr(self, "bg_color"):
self.bg_color = color

View File

@ -111,7 +111,7 @@ from kivy.properties import (
from kivy.uix.behaviors import ToggleButtonBehavior
class CommonRipple(object):
class CommonRipple:
"""Base class for ripple effect."""
ripple_rad_default = NumericProperty(1)

View File

@ -0,0 +1,133 @@
"""
Behaviors/Rotate
================
.. versionadded:: 1.1.0
Base class for controlling the rotate of the widget.
.. note:: See `kivy.graphics.Rotate
<https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Rotate>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.app import App
from kivy.properties import NumericProperty
from kivy.uix.button import Button
KV = '''
Screen:
RotateButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_rotate(self)
canvas.before:
PushMatrix
Rotate:
angle: self.rotate_value_angle
axis: 0, 0, 1
origin: self.center
canvas.after:
PopMatrix
'''
class RotateButton(Button):
rotate_value_angle = NumericProperty(0)
class Test(App):
def build(self):
return Builder.load_string(KV)
def change_rotate(self, instance_button: Button) -> None:
Animation(rotate_value_angle=45, d=0.3).start(instance_button)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivymd.app import MDApp
from kivymd.uix.behaviors import RotateBehavior
from kivymd.uix.boxlayout import MDBoxLayout
KV = '''
MDScreen:
RotateBox:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_rotate(self)
md_bg_color: "red"
'''
class RotateBox(ButtonBehavior, RotateBehavior, MDBoxLayout):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def change_rotate(self, instance_button: RotateBox) -> None:
Animation(rotate_value_angle=45, d=0.3).start(instance_button)
Test().run()
"""
__all__ = ("RotateBehavior",)
from kivy.lang import Builder
from kivy.properties import ListProperty, NumericProperty
Builder.load_string(
"""
<RotateBehavior>
canvas.before:
PushMatrix
Rotate:
angle: self.rotate_value_angle
axis: tuple(self.rotate_value_axis)
origin: self.center
canvas.after:
PopMatrix
"""
)
class RotateBehavior:
"""Base class for controlling the rotate of the widget."""
rotate_value_angle = NumericProperty(0)
"""
Property for getting/setting the angle of the rotation.
:attr:`rotate_value_angle` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
rotate_value_axis = ListProperty((0, 0, 1))
"""
Property for getting/setting the axis of the rotation.
:attr:`rotate_value_axis` is an :class:`~kivy.properties.ListProperty`
and defaults to `(0, 0, 1)`.
"""

View File

@ -0,0 +1,156 @@
"""
Behaviors/Scale
===============
.. versionadded:: 1.1.0
Base class for controlling the scale of the widget.
.. note:: See `kivy.graphics.Rotate
<https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Scale>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.button import Button
from kivy.app import App
KV = '''
Screen:
ScaleButton:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_scale(self)
canvas.before:
PushMatrix
Scale:
x: self.scale_value_x
y: self.scale_value_y
z: self.scale_value_x
origin: self.center
canvas.after:
PopMatrix
'''
class ScaleButton(Button):
scale_value_x = NumericProperty(1)
scale_value_y = NumericProperty(1)
scale_value_z = NumericProperty(1)
class Test(App):
def build(self):
return Builder.load_string(KV)
def change_scale(self, instance_button: Button) -> None:
Animation(
scale_value_x=0.5,
scale_value_y=0.5,
scale_value_z=0.5,
d=0.3,
).start(instance_button)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.uix.behaviors import ButtonBehavior
from kivymd.app import MDApp
from kivymd.uix.behaviors import ScaleBehavior
from kivymd.uix.boxlayout import MDBoxLayout
KV = '''
MDScreen:
ScaleBox:
size_hint: .5, .5
pos_hint: {"center_x": .5, "center_y": .5}
on_release: app.change_scale(self)
md_bg_color: "red"
'''
class ScaleBox(ButtonBehavior, ScaleBehavior, MDBoxLayout):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def change_scale(self, instance_button: ScaleBox) -> None:
Animation(
scale_value_x=0.5,
scale_value_y=0.5,
scale_value_z=0.5,
d=0.3,
).start(instance_button)
Test().run()
"""
__all__ = ("ScaleBehavior",)
from kivy.lang import Builder
from kivy.properties import NumericProperty
Builder.load_string(
"""
<ScaleBehavior>
canvas.before:
PushMatrix
Scale:
x: self.scale_value_x
y: self.scale_value_y
z: self.scale_value_x
origin: self.center
canvas.after:
PopMatrix
"""
)
class ScaleBehavior:
"""Base class for controlling the scale of the widget."""
scale_value_x = NumericProperty(1)
"""
X-axis value.
:attr:`scale_value_x` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
scale_value_y = NumericProperty(1)
"""
Y-axis value.
:attr:`scale_value_y` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""
scale_value_z = NumericProperty(1)
"""
Z-axis value.
:attr:`scale_value_z` is an :class:`~kivy.properties.NumericProperty`
and defaults to `1`.
"""

View File

@ -0,0 +1,134 @@
"""
Behaviors/Stencil
=================
.. versionadded:: 1.1.0
Base class for controlling the stencil instructions of the widget.
.. note:: See `Stencil instructions
<https://kivy.org/doc/stable/api-kivy.graphics.stencil_instructions.html>`_
for more information.
Kivy
----
.. code-block:: python
from kivy.lang import Builder
from kivy.app import App
KV = '''
Carousel:
Button:
size_hint: .9, .8
pos_hint: {"center_x": .5, "center_y": .5}
canvas.before:
StencilPush
RoundedRectangle:
pos: root.pos
size: root.size
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
pos: root.pos
size: root.size
StencilPop
'''
class Test(App):
def build(self):
return Builder.load_string(KV)
Test().run()
KivyMD
------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import StencilBehavior
from kivymd.uix.fitimage import FitImage
KV = '''
#:import os os
#:import images_path kivymd.images_path
MDCarousel:
StencilImage:
size_hint: .9, .8
pos_hint: {"center_x": .5, "center_y": .5}
source: os.path.join(images_path, "logo", "kivymd-icon-512.png")
'''
class StencilImage(FitImage, StencilBehavior):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
Test().run()
"""
__all__ = ("StencilBehavior",)
from kivy.lang import Builder
from kivy.properties import VariableListProperty
Builder.load_string(
"""
<StencilBehavior>
canvas.before:
StencilPush
RoundedRectangle:
pos: root.pos
size: root.size
# FIXME: Sometimes the radius has the value [], which get a
# `GraphicException: Invalid radius value, must be list of tuples/numerics` error
radius: root.radius if root.radius else [0, 0, 0, 0]
StencilUse
canvas.after:
StencilUnUse
RoundedRectangle:
pos: root.pos
size: root.size
# FIXME: Sometimes the radius has the value [], which get a
# `GraphicException: Invalid radius value, must be list of tuples/numerics` error
radius: root.radius if root.radius else [0, 0, 0, 0]
StencilPop
"""
)
class StencilBehavior:
"""Base class for controlling the stencil instructions of the widget."""
radius = VariableListProperty([0], length=4)
"""
Canvas radius.
.. versionadded:: 1.0.0
.. code-block:: python
# Top left corner slice.
MDWidget:
radius: [25, 0, 0, 0]
:attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
and defaults to `[0, 0, 0, 0]`.
"""

View File

@ -14,19 +14,24 @@ example:
pass
.. tabs::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
from kivymd.uix.button import MDRectangleFlatButton
from kivymd.uix.button import MDFlatButton
KV = '''
Screen:
MDScreen:
MDBoxLayout:
adaptive_size: True
spacing: "12dp"
pos_hint: {"center_x": .5, "center_y": .5}
MyToggleButton:
@ -43,30 +48,68 @@ example:
'''
class MyToggleButton(MDRectangleFlatButton, MDToggleButton):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.background_down = self.theme_cls.primary_light
class MyToggleButton(MDFlatButton, MDToggleButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.background_down = self.theme_cls.primary_color
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-1.gif
:align: center
.. tab:: Declarative python style
.. code-block:: python
class MyToggleButton(MDFillRoundFlatButton, MDToggleButton):
def __init__(self, **kwargs):
self.background_down = MDApp.get_running_app().theme_cls.primary_dark
super().__init__(**kwargs)
from kivymd.app import MDApp
from kivymd.uix.behaviors.toggle_behavior import MDToggleButton
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.screen import MDScreen
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-2.gif
class MyToggleButton(MDFlatButton, MDToggleButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.background_down = self.theme_cls.primary_color
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDBoxLayout(
MyToggleButton(
text="Show ads",
group="x",
),
MyToggleButton(
text="Do not show ads",
group="x",
),
MyToggleButton(
text="Does not matter",
group="x",
),
adaptive_size=True,
spacing="12dp",
pos_hint={"center_x": .5, "center_y": .5},
),
)
)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/toggle-button-1.gif
:align: center
You can inherit the ``MyToggleButton`` class only from the following classes
@ -88,6 +131,7 @@ from kivy.properties import BooleanProperty, ColorProperty
from kivy.uix.behaviors import ToggleButtonBehavior
from kivymd.uix.button import (
ButtonContentsIconText,
MDFillRoundFlatButton,
MDFillRoundFlatIconButton,
MDFlatButton,
@ -149,7 +193,8 @@ class MDToggleButton(ToggleButtonBehavior):
# Do the object inherited from the "supported" buttons?
if not issubclass(self.__class__, classinfo):
raise ValueError(
f"Class {self.__class__} must be inherited from one of the classes in the list {classinfo}"
f"Class {self.__class__} must be inherited from one of the "
f"classes in the list {classinfo}"
)
if (
not self.background_normal
@ -165,10 +210,12 @@ class MDToggleButton(ToggleButtonBehavior):
):
self.__is_filled = True
self.background_normal = self.theme_cls.primary_color
# If not the background_normal must be the same as the inherited one:
# If not background_normal must be the same as the inherited one.
else:
self.background_normal = self.md_bg_color[:]
# If no background_down is setted:
self.background_normal = (
self.md_bg_color[:] if self.md_bg_color else (0, 0, 0, 0)
)
# If no background_down is setter.
if (
not self.background_down
): # This means that if the value == [] or None will return True.
@ -200,3 +247,6 @@ class MDToggleButton(ToggleButtonBehavior):
): # If the background is transparent, the font color must be the
# primary color.
self.text_color = self.font_color_normal
if issubclass(self.__class__, ButtonContentsIconText):
self.icon_color = self.text_color

View File

@ -1,4 +1,3 @@
#:import sm kivy.uix.screenmanager
#:import STANDARD_INCREMENT kivymd.material_resources.STANDARD_INCREMENT
@ -9,7 +8,7 @@
ScreenManager:
id: tab_manager
transition: sm.FadeTransition(duration=.2)
transition: root.transition(duration=root.transition_duration)
on_current:
root.dispatch( \
"on_switch_tabs", \
@ -96,7 +95,7 @@
radius: [16,]
size: root._selected_region_width, dp(32)
pos:
self.center_x - self.width - dp(8), \
self.center_x - root._selected_region_width / 2, \
self.center_y - (dp(16))
MDLabel:

View File

@ -62,6 +62,10 @@ For ease of understanding, this code works like this:
Example
-------
.. tabs::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
@ -73,14 +77,15 @@ Example
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.theme_style = "Dark"
return Builder.load_string(
'''
MDScreen:
MDBottomNavigation:
panel_color: "#eeeaea"
selected_color_background: "#97ecf8"
text_color_active: 0, 0, 0, 1
#panel_color: "#eeeaea"
selected_color_background: "orange"
text_color_active: "lightgrey"
MDBottomNavigationItem:
name: 'screen 1'
@ -94,12 +99,12 @@ Example
MDBottomNavigationItem:
name: 'screen 2'
text: 'Discord'
icon: 'discord'
text: 'Twitter'
icon: 'twitter'
badge_icon: "numeric-5"
MDLabel:
text: 'Discord'
text: 'Twitter'
halign: 'center'
MDBottomNavigationItem:
@ -116,6 +121,62 @@ Example
Test().run()
.. tab:: Declarative python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.bottomnavigation import MDBottomNavigation, MDBottomNavigationItem
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
class Test(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
self.theme_cls.theme_style = "Dark"
return (
MDScreen(
MDBottomNavigation(
MDBottomNavigationItem(
MDLabel(
text='Mail',
halign='center',
),
name='screen 1',
text='Mail',
icon='gmail',
badge_icon="numeric-10",
),
MDBottomNavigationItem(
MDLabel(
text='Twitter',
halign='center',
),
name='screen 1',
text='Twitter',
icon='twitter',
badge_icon="numeric-10",
),
MDBottomNavigationItem(
MDLabel(
text='LinkedIN',
halign='center',
),
name='screen 1',
text='LinkedIN',
icon='linkedin',
badge_icon="numeric-10",
),
selected_color_background="orange",
text_color_active="lightgrey",
)
)
)
Test().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/bottom-navigation.gif
:align: center
@ -192,13 +253,13 @@ from kivy.properties import (
)
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManagerException
from kivy.uix.screenmanager import FadeTransition, ScreenManagerException
from kivymd import uix_path
from kivymd.material_resources import STANDARD_INCREMENT
from kivymd.theming import ThemableBehavior, ThemeManager
from kivymd.uix.anchorlayout import MDAnchorLayout
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.behaviors import CommonElevationBehavior, DeclarativeBehavior
from kivymd.uix.behaviors.backgroundcolor_behavior import (
SpecificBackgroundColorBehavior,
)
@ -364,8 +425,8 @@ class MDTab(MDScreen, ThemableBehavior):
and defaults to `''`.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.index = 0
self.parent_widget = None
self.register_event_type("on_tab_touch_down")
@ -407,16 +468,12 @@ class MDBottomNavigationItem(MDTab):
and defaults to `None`.
"""
def on_tab_press(self, *args) -> None:
"""Called when clicking on a panel item."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
bottom_navigation_object = self.parent_widget
bottom_navigation_header_object = (
bottom_navigation_object.previous_tab.header
)
bottom_navigation_object.ids.tab_manager.current = self.name
if bottom_navigation_object.previous_tab is not self:
def animate_header(
self, bottom_navigation_object, bottom_navigation_header_object
) -> None:
if bottom_navigation_object.use_text:
Animation(_label_font_size=sp(12), d=0.1).start(
bottom_navigation_object.previous_tab.header
@ -435,7 +492,21 @@ class MDBottomNavigationItem(MDTab):
).start(bottom_navigation_object.previous_tab.header)
bottom_navigation_object.previous_tab.header.active = False
self.header.active = True
bottom_navigation_object.previous_tab = self
def on_tab_press(self, *args) -> None:
"""Called when clicking on a panel item."""
bottom_navigation_object = self.parent_widget
bottom_navigation_header_object = (
bottom_navigation_object.previous_tab.header
)
if bottom_navigation_object.previous_tab is not self:
self.animate_header(
bottom_navigation_object, bottom_navigation_header_object
)
super().on_tab_press(*args)
def on_disabled(
self, instance_bottom_navigation_item, disabled_value: bool
@ -463,7 +534,7 @@ class TabbedPanelBase(
and defaults to `None`.
"""
previous_tab = ObjectProperty()
previous_tab = ObjectProperty(None, aloownone=True)
"""
:attr:`previous_tab` is an :class:`~MDTab` and defaults to `None`.
"""
@ -479,7 +550,7 @@ class TabbedPanelBase(
tabs = ListProperty()
class MDBottomNavigation(TabbedPanelBase):
class MDBottomNavigation(DeclarativeBehavior, TabbedPanelBase):
"""
A bottom navigation that is implemented by delegating all items to a
:class:`~kivy.uix.screenmanager.ScreenManager`.
@ -492,6 +563,26 @@ class MDBottomNavigation(TabbedPanelBase):
.. versionadded:: 1.0.0
"""
transition = ObjectProperty(FadeTransition)
"""
Transition animation of bottom navigation screen manager.
.. versionadded:: 1.1.0
:attr:`transition` is an :class:`~kivy.properties.ObjectProperty`
and defaults to `FadeTransition`.
"""
transition_duration = NumericProperty(0.2)
"""
Duration animation of bottom navigation screen manager.
.. versionadded:: 1.1.0
:attr:`transition_duration` is an :class:`~kivy.properties.NumericProperty`
and defaults to `0.2`.
"""
text_color_normal = ColorProperty([1, 1, 1, 1])
"""
Text color of the label when it is not selected.
@ -594,14 +685,15 @@ class MDBottomNavigation(TabbedPanelBase):
and defaults to `False`.
"""
widget_index = NumericProperty(0)
# Text active color if it is selected.
_active_color = ColorProperty([1, 1, 1, 1])
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_switch_tabs")
def __init__(self, *args, **kwargs):
self.previous_tab = None
self.widget_index = 0
self.register_event_type("on_switch_tabs")
super().__init__(*args, **kwargs)
self.theme_cls.bind(material_style=self.refresh_tabs)
Window.bind(on_resize=self.on_resize)
Clock.schedule_once(lambda x: self.on_resize())
@ -660,9 +752,12 @@ class MDBottomNavigation(TabbedPanelBase):
def on_selected_color_background(
self, instance_bottom_navigation, color: list
) -> None:
def on_selected_color_background(*args):
for tab in self.ids.tab_bar.children:
tab.selected_color_background = color
Clock.schedule_once(on_selected_color_background)
def on_use_text(
self, instance_bottom_navigation, use_text_value: bool
) -> None:
@ -698,6 +793,7 @@ class MDBottomNavigation(TabbedPanelBase):
def on_text_color_active(
self, instance_bottom_navigation, color: list
) -> None:
def on_text_color_active(*args):
MDBottomNavigationHeader.text_color_active = color
self.text_color_active = color
for tab in self.ids.tab_bar.children:
@ -705,6 +801,8 @@ class MDBottomNavigation(TabbedPanelBase):
if tab.active:
tab._text_color_normal = color
Clock.schedule_once(on_text_color_active)
def on_switch_tabs(self, bottom_navigation_item, name_tab: str) -> None:
"""
Called when switching tabs. Returns the object of the tab to be opened.
@ -759,8 +857,6 @@ class MDBottomNavigation(TabbedPanelBase):
class MDBottomNavigationBar(
ThemableBehavior,
FakeRectangularElevationBehavior,
MDFloatLayout,
ThemableBehavior, CommonElevationBehavior, MDFloatLayout
):
pass

View File

@ -34,7 +34,7 @@ Usage :class:`~MDListBottomSheet`
MDTopAppBar:
title: "Example BottomSheet"
pos_hint: {"top": 1}
elevation: 10
elevation: 4
MDRaisedButton:
text: "Open list bottom sheet"
@ -94,7 +94,7 @@ which will be used as an icon to the left of the item:
MDTopAppBar:
title: 'Example BottomSheet'
pos_hint: {"top": 1}
elevation: 10
elevation: 4
MDRaisedButton:
text: "Open grid bottom sheet"
@ -180,7 +180,7 @@ which will be used as an icon to the left of the item:
MDTopAppBar:
title: 'Example BottomSheet'
pos_hint: {"top": 1}
elevation: 10
elevation: 4
MDRaisedButton:
text: "Open custom bottom sheet"

View File

@ -8,7 +8,7 @@ with some widget properties. For example:
BoxLayout
---------
.. code-block::
.. code-block:: kv
BoxLayout:
size_hint_y: None
@ -24,7 +24,7 @@ BoxLayout
MDBoxLayout
-----------
.. code-block::
.. code-block:: kv
MDBoxLayout:
adaptive_height: True
@ -88,7 +88,13 @@ __all__ = ("MDBoxLayout",)
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix import MDAdaptiveWidget
from kivymd.uix.behaviors import DeclarativeBehavior
class MDBoxLayout(BoxLayout, MDAdaptiveWidget):
pass
class MDBoxLayout(DeclarativeBehavior, BoxLayout, MDAdaptiveWidget):
"""
Box layout class.
For more information, see in the
:class:`~kivy.uix.boxlayout.BoxLayout` class documentation.
"""

View File

@ -1,6 +1,7 @@
# NOQA F401
from .button import (
BaseButton,
ButtonContentsIconText,
MDFillRoundFlatButton,
MDFillRoundFlatIconButton,
MDFlatButton,

View File

@ -3,9 +3,9 @@
Clear
Color:
rgba:
(self._md_bg_color or [0.0, 0.0, 0.0, 0.0]) \
self._md_bg_color \
if not self.disabled else \
(self._md_bg_color_disabled or [0.0, 0.0, 0.0, 0.0])
self._md_bg_color_disabled
RoundedRectangle:
size: self.size
pos: self.pos
@ -13,19 +13,17 @@
radius: [root._radius, ]
Color:
rgba:
root._line_color or [0.0, 0.0, 0.0, 0.0] \
root._line_color \
if not root.disabled else \
( \
root._line_color_disabled \
or self._disabled_color \
or [0.0, 0.0, 0.0, 0.0] \
)
(root._line_color_disabled or self._disabled_color)
Line:
width: root.line_width
rounded_rectangle:
(self.x, self.y, self.width, self.height, \
( \
self.x, self.y, self.width, self.height, \
root._radius, root._radius, root._radius, root._radius, \
self.height)
self.height \
)
size_hint: None, None
anchor_x: root.halign
@ -33,21 +31,28 @@
_round_rad: [self._radius] * 4
<ButtonContentsText>
lbl_txt: lbl_txt
width:
max(root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2])
max( \
root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2] \
)
size_hint_min_x:
max(root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2])
max( \
root._min_width, \
root.padding[0] + lbl_txt.texture_size[0] + root.padding[2] \
)
height:
max(root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3])
max( \
root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3] \
)
size_hint_min_y:
max(root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3])
max( \
root._min_height, \
root.padding[1] + lbl_txt.texture_size[1] + root.padding[3] \
)
MDLabel:
id: lbl_txt
@ -84,7 +89,10 @@
# This is only a temporary fix and does not fix the cause of the error.
(root._icon_color if root._icon_color else root.theme_cls.text_color) \
if not root.disabled else \
root.theme_cls.disabled_hint_text_color
root.theme_cls.disabled_hint_text_color \
if not root.disabled_color else \
root.disabled_color
on_icon:
if self.icon not in md_icons.keys(): self.size_hint = (1, 1)
theme_text_color: root._theme_icon_color
@ -131,7 +139,7 @@
id: box
adaptive_size: True
padding: 0
spacing: "4dp"
spacing: "8dp"
MDIcon:
id: lbl_ic
@ -193,48 +201,21 @@
radius: [self.height / 2]
<BaseFloatingRootButton>
<MDFloatingRootButton>
theme_text_color: "Custom"
md_bg_color: self.theme_cls.primary_color
<MDFloatingLabel>
padding_x: "8dp"
padding_y: "8dp"
adaptive_size: True
theme_text_color: "Custom"
canvas.before:
PushMatrix
Rotate:
angle: self._angle
axis: (0, 0, 1)
origin: self.center
canvas.after:
PopMatrix
# FIXME: Use :class:`~kivymd.uix.boxlayout.MDBoxLayout` instead
# :class:`~kivy.uix.boxlayout.BoxLayout`.
<BaseFloatingLabel>
size_hint: None, None
padding: "8dp", "4dp", "8dp", "4dp"
height: label.texture_size[1] + self.padding[1] * 2
width: label.texture_size[0] + self.padding[0] * 2
elevation: 10
# TODO: Use `md_bg_color` and `radius` instead `canvasю
canvas:
Color:
rgba:
self.theme_cls.primary_color \
if not root.bg_color else \
root.bg_color
rgba: self.bg_color
RoundedRectangle:
pos: self.pos
size: self.size
radius: [5]
Label:
id: label
markup: True
text: root.text
size_hint: None, None
size: self.texture_size
color:
root.theme_cls.text_color \
if not root.text_color else \
root.text_color
pos: self.pos
radius: self.radius

File diff suppressed because it is too large Load Diff

View File

@ -2,17 +2,6 @@
md_bg_color: app.theme_cls.divider_color
<MDCard>
canvas.before:
Color:
rgba: self.md_bg_color
RoundedRectangle:
size: self.size
pos: self.pos
radius: root.radius
source: root.background
<MDSeparator>
md_bg_color:
self.theme_cls.divider_color \

View File

@ -26,58 +26,40 @@ Components/Card
MDCard
------
.. warning:: Starting from the KivyMD 1.0.0 library version, it is necessary
to manually inherit the card class from one of the ``Elevation`` classes
from ``kivymd/uix/behaviors/elevation.py`` module to draw the card shadow.
.. code-block:: python
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
'''Implements a material design v3 card.'''
This may sound awkward to you, but it actually allows for better control over
the providers that implement the rendering of the shadows.
.. note:: You can read more information about the classes that implement the
rendering of shadows on this `documentation page <https://kivymd.readthedocs.io/en/latest/behaviors/elevation/>`_.
An example of the implementation of a card in the style of material design version 3
------------------------------------------------------------------------------------
.. tabs::
.. tab:: Declarative KV and imperative python styles
.. code-block:: python
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivymd.uix.behaviors import RoundedRectangularElevationBehavior
from kivymd.uix.card import MDCard
KV = '''
<MD3Card>
padding: 16
padding: 4
size_hint: None, None
size: "200dp", "100dp"
MDRelativeLayout:
size_hint: None, None
size: root.size
MDIconButton:
icon: "dots-vertical"
pos:
root.width - (self.width + root.padding[0] + dp(4)), \
root.height - (self.height + root.padding[0] + dp(4))
pos_hint: {"top": 1, "right": 1}
MDLabel:
id: label
text: root.text
adaptive_size: True
color: .2, .2, .2, .8
color: "grey"
pos: "12dp", "12dp"
bold: True
MDScreen:
@ -90,13 +72,13 @@ An example of the implementation of a card in the style of material design versi
'''
class MD3Card(MDCard, RoundedRectangularElevationBehavior):
class MD3Card(MDCard):
'''Implements a material design v3 card.'''
text = StringProperty()
class TestCard(MDApp):
class Example(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
return Builder.load_string(KV)
@ -116,7 +98,69 @@ An example of the implementation of a card in the style of material design versi
)
TestCard().run()
Example().run()
.. tab:: Declarative python styles
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDIconButton
from kivymd.uix.card import MDCard
from kivymd.uix.label import MDLabel
from kivymd.uix.relativelayout import MDRelativeLayout
from kivymd.uix.screen import MDScreen
class MD3Card(MDCard):
'''Implements a material design v3 card.'''
class Example(MDApp):
def build(self):
self.theme_cls.material_style = "M3"
return (
MDScreen(
MDBoxLayout(
id="box",
adaptive_size=True,
spacing="56dp",
pos_hint={"center_x": 0.5, "center_y": 0.5},
)
)
)
def on_start(self):
styles = {
"elevated": "#f6eeee", "filled": "#f4dedc", "outlined": "#f8f5f4"
}
for style in styles.keys():
self.root.ids.box.add_widget(
MD3Card(
MDRelativeLayout(
MDIconButton(
icon="dots-vertical",
pos_hint={"top": 1, "right": 1}
),
MDLabel(
text=style.capitalize(),
adaptive_size=True,
color="grey",
pos=("12dp", "12dp"),
),
),
line_color=(0.2, 0.2, 0.2, 0.8),
style=style,
padding="4dp",
size_hint=(None, None),
size=("200dp", "100dp"),
md_bg_color=styles[style],
)
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/cards-m3.png
:align: center
@ -125,16 +169,13 @@ An example of the implementation of a card in the style of material design versi
MDCardSwipe
-----------
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/MDCardSwipe.gif
:align: center
To create a card with `swipe-to-delete` behavior, you must create a new class
that inherits from the :class:`~MDCardSwipe` class:
.. code-block:: kv
<SwipeToDeleteItem>:
<SwipeToDeleteItem>
size_hint_y: None
height: content.height
@ -152,12 +193,16 @@ that inherits from the :class:`~MDCardSwipe` class:
class SwipeToDeleteItem(MDCardSwipe):
text = StringProperty()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/map-mdcard-swipr.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/sceleton-mdcard-swiper.png
:align: center
End full code
-------------
.. tabs::
.. tab:: Declarative KV and imperative python styles
.. code-block:: python
from kivy.lang import Builder
@ -167,7 +212,7 @@ End full code
from kivymd.uix.card import MDCardSwipe
KV = '''
<SwipeToDeleteItem>:
<SwipeToDeleteItem>
size_hint_y: None
height: content.height
@ -187,13 +232,12 @@ End full code
MDBoxLayout:
orientation: "vertical"
spacing: "10dp"
MDTopAppBar:
elevation: 10
elevation: 4
title: "MDCardSwipe"
ScrollView:
MDScrollView:
scroll_timeout : 100
MDList:
@ -208,24 +252,83 @@ End full code
text = StringProperty()
class TestCard(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_string(KV)
class Example(MDApp):
def build(self):
return self.screen
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def on_start(self):
'''Creates a list of cards.'''
for i in range(20):
self.screen.ids.md_list.add_widget(
self.root.ids.md_list.add_widget(
SwipeToDeleteItem(text=f"One-line item {i}")
)
TestCard().run()
Example().run()
.. tab:: Declarative python styles
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.card import (
MDCardSwipe, MDCardSwipeLayerBox, MDCardSwipeFrontBox
)
from kivymd.uix.list import MDList, OneLineListItem
from kivymd.uix.screen import MDScreen
from kivymd.uix.scrollview import MDScrollView
from kivymd.uix.toolbar import MDTopAppBar
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDBoxLayout(
MDTopAppBar(
elevation=4,
title="MDCardSwipe",
),
MDScrollView(
MDList(
id="md_list",
),
id="scroll",
scroll_timeout=100,
),
id="box",
orientation="vertical",
),
)
)
def on_start(self):
'''Creates a list of cards.'''
for i in range(20):
self.root.ids.box.ids.scroll.ids.md_list.add_widget(
MDCardSwipe(
MDCardSwipeLayerBox(),
MDCardSwipeFrontBox(
OneLineListItem(
id="content",
text=f"One-line item {i}",
_no_ripple_effect=True,
)
),
size_hint_y=None,
height="52dp",
)
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/list-mdcard-swipe.gif
:align: center
@ -235,7 +338,7 @@ Binding a swipe to one of the sides of the screen
.. code-block:: kv
<SwipeToDeleteItem>:
<SwipeToDeleteItem>
# By default, the parameter is "left"
anchor: "right"
@ -250,7 +353,7 @@ Swipe behavior
.. code-block:: kv
<SwipeToDeleteItem>:
<SwipeToDeleteItem>
# By default, the parameter is "hand"
type_swipe: "hand"
@ -271,85 +374,41 @@ Removing an item using the ``type_swipe = "auto"`` parameter
The map provides the :attr:`MDCardSwipe.on_swipe_complete` event.
You can use this event to remove items from a list:
.. tabs::
.. tab:: Declarative KV styles
.. code-block:: kv
<SwipeToDeleteItem>:
on_swipe_complete: app.on_swipe_complete(root)
.. code-block:: python
.. tab:: Declarative python styles
def on_swipe_complete(self, instance):
self.screen.ids.md_list.remove_widget(instance)
End full code
-------------
.. code-block:: kv
.. code-block:: python
from kivy.lang import Builder
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivymd.uix.card import MDCardSwipe
KV = '''
<SwipeToDeleteItem>:
size_hint_y: None
height: content.height
type_swipe: "auto"
on_swipe_complete: app.on_swipe_complete(root)
MDCardSwipeLayerBox:
MDCardSwipeFrontBox:
OneLineListItem:
id: content
text: root.text
_no_ripple_effect: True
MDScreen:
MDBoxLayout:
orientation: "vertical"
spacing: "10dp"
MDTopAppBar:
elevation: 10
title: "MDCardSwipe"
ScrollView:
MDList:
id: md_list
padding: 0
'''
class SwipeToDeleteItem(MDCardSwipe):
text = StringProperty()
class TestCard(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen = Builder.load_string(KV)
def build(self):
return self.screen
def on_swipe_complete(self, instance):
self.screen.ids.md_list.remove_widget(instance)
def on_start(self):
for i in range(20):
self.screen.ids.md_list.add_widget(
SwipeToDeleteItem(text=f"One-line item {i}")
MDCardSwipe(
...
on_swipe_complete=self.on_swipe_complete,
)
.. tabs::
TestCard().run()
.. tab:: Imperative python styles
.. code-block:: python
def on_swipe_complete(self, instance):
self.root.ids.md_list.remove_widget(instance)
.. tab:: Decralative python styles
.. code-block:: python
def on_swipe_complete(self, instance):
self.root.ids.box.ids.scroll.ids.md_list.remove_widget(instance)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/autodelete-mdcard-swipe.gif
:align: center
@ -375,6 +434,10 @@ use the :class:`~MDCardSwipeLayerBox` class.
End full code
-------------
.. tabs::
.. tab:: Declarative KV styles
.. code-block:: python
from kivy.lang import Builder
@ -408,13 +471,12 @@ End full code
MDBoxLayout:
orientation: "vertical"
spacing: "10dp"
MDTopAppBar:
elevation: 10
elevation: 4
title: "MDCardSwipe"
ScrollView:
MDScrollView:
MDList:
id: md_list
@ -426,9 +488,11 @@ End full code
text = StringProperty()
class TestCard(MDApp):
class Example(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
self.screen = Builder.load_string(KV)
def build(self):
@ -444,7 +508,80 @@ End full code
)
TestCard().run()
Example().run()
.. tab:: Decralative python styles
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDIconButton
from kivymd.uix.card import (
MDCardSwipe, MDCardSwipeLayerBox, MDCardSwipeFrontBox
)
from kivymd.uix.list import MDList, OneLineListItem
from kivymd.uix.screen import MDScreen
from kivymd.uix.scrollview import MDScrollView
from kivymd.uix.toolbar import MDTopAppBar
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDScreen(
MDBoxLayout(
MDTopAppBar(
elevation=4,
title="MDCardSwipe",
),
MDScrollView(
MDList(
id="md_list",
),
id="scroll",
scroll_timeout=100,
),
id="box",
orientation="vertical",
),
)
)
def on_start(self):
'''Creates a list of cards.'''
for i in range(20):
self.root.ids.box.ids.scroll.ids.md_list.add_widget(
MDCardSwipe(
MDCardSwipeLayerBox(
MDIconButton(
icon="trash-can",
pos_hint={"center_y": 0.5},
on_release=self.remove_item,
),
),
MDCardSwipeFrontBox(
OneLineListItem(
id="content",
text=f"One-line item {i}",
_no_ripple_effect=True,
)
),
size_hint_y=None,
height="52dp",
)
)
def remove_item(self, instance):
self.root.ids.box.ids.scroll.ids.md_list.remove_widget(
instance.parent.parent
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/handdelete-mdcard-swipe.gif
:align: center
@ -457,6 +594,67 @@ Focus behavior
MDCard:
focus_behavior: True
.. tabs::
.. tab:: Declarative KV styles
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
MDScreen:
MDCard:
size_hint: .7, .4
focus_behavior: True
pos_hint: {"center_x": .5, "center_y": .5}
md_bg_color: "darkgrey"
unfocus_color: "darkgrey"
focus_color: "grey"
elevation: 6
'''
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return Builder.load_string(KV)
Example().run()
.. tab:: Declarative python styles
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.card import MDCard
from kivymd.uix.screen import MDScreen
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return (
MDScreen(
MDCard(
size_hint=(0.7, 0.4),
focus_behavior=True,
pos_hint={"center_x": 0.5, "center_y": 0.5},
md_bg_color="darkgrey",
unfocus_color="darkgrey",
focus_color="grey",
elevation=6,
),
)
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/card-focus.gif
:align: center
@ -471,88 +669,6 @@ Ripple behavior
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/card-behavior.gif
:align: center
End full code
-------------
.. code-block:: python
from kivy.lang import Builder
from kivymd.app import MDApp
KV = '''
<StarButton@MDIconButton>
icon: "star"
on_release: self.icon = "star-outline" if self.icon == "star" else "star"
MDScreen:
MDCard:
orientation: "vertical"
size_hint: .5, None
height: box_top.height + box_bottom.height
focus_behavior: True
ripple_behavior: True
pos_hint: {"center_x": .5, "center_y": .5}
MDBoxLayout:
id: box_top
spacing: "20dp"
adaptive_height: True
FitImage:
source: "/Users/macbookair/album.jpeg"
size_hint: .3, None
height: text_box.height
MDBoxLayout:
id: text_box
orientation: "vertical"
adaptive_height: True
spacing: "10dp"
padding: 0, "10dp", "10dp", "10dp"
MDLabel:
text: "Ride the Lightning"
theme_text_color: "Primary"
font_style: "H5"
bold: True
adaptive_height: True
MDLabel:
text: "July 27, 1984"
adaptive_height: True
theme_text_color: "Primary"
MDSeparator:
MDBoxLayout:
id: box_bottom
adaptive_height: True
padding: "10dp", 0, 0, 0
MDLabel:
text: "Rate this album"
adaptive_height: True
pos_hint: {"center_y": .5}
theme_text_color: "Primary"
StarButton:
StarButton:
StarButton:
StarButton:
StarButton:
'''
class Test(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
return Builder.load_string(KV)
Test().run()
"""
__all__ = (
@ -579,17 +695,20 @@ from kivy.properties import (
VariableListProperty,
)
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.utils import get_color_from_hex
from kivymd import uix_path
from kivymd.color_definitions import colors
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
BackgroundColorBehavior,
FocusBehavior,
CommonElevationBehavior,
DeclarativeBehavior,
RectangularRippleBehavior,
)
from kivymd.uix.behaviors.focus_behavior import FocusBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.relativelayout import MDRelativeLayout
with open(
os.path.join(uix_path, "card", "card.kv"), encoding="utf-8"
@ -602,7 +721,7 @@ class MDSeparator(ThemableBehavior, MDBoxLayout):
color = ColorProperty(None)
"""
Separator color in ``rgba`` format.
Separator color.
:attr:`color` is a :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
@ -623,9 +742,11 @@ class MDSeparator(ThemableBehavior, MDBoxLayout):
class MDCard(
DeclarativeBehavior,
ThemableBehavior,
BackgroundColorBehavior,
RectangularRippleBehavior,
CommonElevationBehavior,
FocusBehavior,
BoxLayout,
):
@ -645,14 +766,6 @@ class MDCard(
and defaults to `False`.
"""
elevation = NumericProperty(None, allownone=True)
"""
Elevation value.
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to 1.
"""
radius = VariableListProperty([dp(6), dp(6), dp(6), dp(6)])
"""
Card radius by default.
@ -676,15 +789,16 @@ class MDCard(
"""
_bg_color_map = (
colors["Light"]["CardsDialogs"],
colors["Dark"]["CardsDialogs"],
get_color_from_hex(colors["Light"]["CardsDialogs"]),
get_color_from_hex(colors["Dark"]["CardsDialogs"]),
[1.0, 1.0, 1.0, 0.0],
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.theme_cls.bind(theme_style=self.update_md_bg_color)
self.theme_cls.bind(material_style=self.set_style)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.theme_cls.bind(
material_style=self.set_style, theme_style=self.update_md_bg_color
)
Clock.schedule_once(self.set_style)
Clock.schedule_once(
lambda x: self.on_ripple_behavior(0, self.ripple_behavior)
@ -693,24 +807,26 @@ class MDCard(
def update_md_bg_color(self, instance_card, theme_style: str) -> None:
if self.md_bg_color in self._bg_color_map:
self.md_bg_color = colors[theme_style]["CardsDialogs"]
self.md_bg_color = get_color_from_hex(
colors[theme_style]["CardsDialogs"]
)
def set_style(self, *args) -> None:
self.set_radius()
self.set_elevation()
self.set_line_color()
def set_line_color(self):
def set_line_color(self) -> None:
if self.theme_cls.material_style == "M3":
if self.style == "elevated" or self.style == "filled":
self.line_color = [0, 0, 0, 0]
def set_elevation(self):
def set_elevation(self) -> None:
if self.theme_cls.material_style == "M3":
if self.style == "outlined" or self.style == "filled":
self.elevation = 0
elif self.style == "elevated":
self.elevation = 1
self.elevation = 2
def set_radius(self) -> None:
if (
@ -730,7 +846,7 @@ class MDCard(
self._no_ripple_effect = False if value_behavior else True
class MDCardSwipe(RelativeLayout):
class MDCardSwipe(MDRelativeLayout):
"""
:Events:
:attr:`on_swipe_complete`
@ -765,6 +881,16 @@ class MDCardSwipe(RelativeLayout):
and defaults to `'out_sine'`.
"""
closing_interval = NumericProperty(0)
"""
Interval for closing the front layer.
.. versionadded:: 1.1.0
:attr:`closing_interval` is a :class:`~kivy.properties.NumericProperty`
and defaults to `0`.
"""
anchor = OptionProperty("left", options=("left", "right"))
"""
Anchoring screen edge for card. Available options are: `'left'`, `'right'`.
@ -830,10 +956,11 @@ class MDCardSwipe(RelativeLayout):
_opens_process = False
_to_closed = True
_distance = 0
def __init__(self, **kw):
def __init__(self, *args, **kwargs):
self.register_event_type("on_swipe_complete")
super().__init__(**kw)
super().__init__(*args, **kwargs)
def add_widget(self, widget, index=0, canvas=None):
if isinstance(widget, (MDCardSwipeFrontBox, MDCardSwipeLayerBox)):
@ -853,18 +980,24 @@ class MDCardSwipe(RelativeLayout):
def on_open_progress(
self, instance_swipe_to_delete_item, progress_value: float
) -> None:
def on_open_progress(*args):
if self.anchor == "left":
self.children[0].x = self.width * progress_value
else:
self.children[0].x = self.width * progress_value - self.width
Clock.schedule_once(on_open_progress)
def on_touch_move(self, touch):
if self.collide_point(touch.x, touch.y):
expr = (
touch.x < self.swipe_distance
if self.anchor == "left"
else touch.x > self.width - self.swipe_distance
)
self._distance += touch.dx
expr = False
if self.anchor == "left" and touch.dx >= 0:
expr = abs(self._distance) < self.swipe_distance
elif self.anchor == "right" and touch.dx < 0:
expr = abs(self._distance) > self.swipe_distance
if expr and not self._opens_process:
self._opens_process = True
self._to_closed = False
@ -875,6 +1008,7 @@ class MDCardSwipe(RelativeLayout):
return super().on_touch_move(touch)
def on_touch_up(self, touch):
self._distance = 0
if self.collide_point(touch.x, touch.y):
if not self._to_closed:
self._opens_process = False
@ -885,7 +1019,7 @@ class MDCardSwipe(RelativeLayout):
if self.collide_point(touch.x, touch.y):
if self.state == "opened":
self._to_closed = True
self.close_card()
Clock.schedule_once(self.close_card, self.closing_interval)
return super().on_touch_down(touch)
def complete_swipe(self) -> None:
@ -895,7 +1029,7 @@ class MDCardSwipe(RelativeLayout):
else self.open_progress >= self.max_swipe_x
)
if expr:
self.close_card()
Clock.schedule_once(self.close_card, self.closing_interval)
else:
self.open_card()
@ -915,7 +1049,7 @@ class MDCardSwipe(RelativeLayout):
anim.start(self.children[0])
self.state = "opened"
def close_card(self) -> None:
def close_card(self, *args) -> None:
anim = Animation(x=0, t=self.closing_transition, d=self.opening_time)
anim.bind(on_complete=self._reset_open_progress)
anim.start(self.children[0])

View File

@ -57,8 +57,10 @@ MDCarousel
from kivy.animation import Animation
from kivy.uix.carousel import Carousel
from kivymd.uix.behaviors import DeclarativeBehavior
class MDCarousel(Carousel):
class MDCarousel(DeclarativeBehavior, Carousel):
"""
based on kivy's carousel.
@ -68,8 +70,8 @@ class MDCarousel(Carousel):
_scrolling = False
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.register_event_type("on_slide_progress")
self.register_event_type("on_slide_complete")

View File

@ -1 +1 @@
from .chip import MDChip, MDChooseChip # NOQA F401
from .chip import MDChip # NOQA F401

View File

@ -132,7 +132,7 @@ Use with elevation
icon_right: "close-circle-outline"
line_color: app.theme_cls.disabled_hint_text_color
md_bg_color: 1, 0, 0, .5
elevation: 12
elevation: 4
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/chip-with-elevation.png
:align: center
@ -304,7 +304,6 @@ __all__ = ("MDChip",)
import os
from kivy import Logger
from kivy.animation import Animation
from kivy.lang import Builder
from kivy.metrics import dp
@ -314,14 +313,13 @@ from kivy.uix.behaviors import ButtonBehavior
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import (
FakeRectangularElevationBehavior,
CommonElevationBehavior,
RectangularRippleBehavior,
ScaleBehavior,
TouchBehavior,
)
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.label import MDIcon
from kivymd.uix.stacklayout import MDStackLayout
from kivymd.uix.templates import ScaleWidget
with open(
os.path.join(uix_path, "chip", "chip.kv"), encoding="utf-8"
@ -330,12 +328,12 @@ with open(
class MDChip(
MDBoxLayout,
ThemableBehavior,
RectangularRippleBehavior,
FakeRectangularElevationBehavior,
TouchBehavior,
ButtonBehavior,
MDBoxLayout,
CommonElevationBehavior,
TouchBehavior,
):
text = StringProperty()
"""
@ -345,17 +343,6 @@ class MDChip(
and defaults to `''`.
"""
icon = StringProperty("checkbox-blank-circle", deprecated=True)
"""
Chip icon.
.. deprecated:: 1.0.0
Use :attr:`icon_right` and :attr:`icon_left` instead.
:attr:`icon` is an :class:`~kivy.properties.StringProperty`
and defaults to `'checkbox-blank-circle'`.
"""
icon_left = StringProperty()
"""
Chip left icon.
@ -376,16 +363,6 @@ class MDChip(
and defaults to `''`.
"""
color = ColorProperty(None, deprecated=True)
"""
Chip color in ``rgba`` format.
.. deprecated:: 1.0.0
:attr:`color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
text_color = ColorProperty(None)
"""
Chip's text color in ``rgba`` format.
@ -394,17 +371,6 @@ class MDChip(
and defaults to `None`.
"""
icon_color = ColorProperty(None, deprecated=True)
"""
Chip's icon color in ``rgba`` format.
.. deprecated:: 1.0.0
Use :attr:`icon_right_color` and :attr:`icon_left_color` instead.
:attr:`icon_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
icon_right_color = ColorProperty(None)
"""
Chip's right icon color in ``rgba`` format.
@ -435,26 +401,6 @@ class MDChip(
and defaults to `None`.
"""
check = BooleanProperty(False, deprecated=True)
"""
If `True`, a checkmark is added to the left when touch to the chip.
.. deprecated:: 1.0.0
:attr:`check` is an :class:`~kivy.properties.BooleanProperty`
and defaults to `False`.
"""
selected_chip_color = ColorProperty(None, deprecated=True)
"""
The color of the chip that is currently selected in ``rgba`` format.
.. deprecated:: 1.0.0
:attr:`selected_chip_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.
"""
active = BooleanProperty(False)
"""
Whether the check is marked or not.
@ -508,16 +454,7 @@ class MDChip(
self.active = False
class MDChooseChip(MDStackLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Logger.warning(
"MDChooseChip: "
"class is deprecated and will be removed in a future version"
)
class MDScalableCheckIcon(MDIcon, ScaleWidget):
class MDScalableCheckIcon(MDIcon, ScaleBehavior):
pos_hint = {"center_y": 0.5}

View File

@ -2,6 +2,8 @@
Controllers/WindowController
============================
.. versionadded:: 1.0.0
Modules and classes that implement useful methods for getting information
about the state of the current application window.
@ -33,16 +35,35 @@ Controlling the resizing direction of the application window
from kivy.core.window import Window
from kivy.core.window.window_sdl2 import WindowSDL
from kivy.metrics import dp
class WindowController:
def __init__(self):
self.window_resizing_direction = "unknown"
self.real_device_type = "unknown"
self.__width = Window.width
Window.bind(on_resize=self._on_resize)
def on_size(self, instance, size: list) -> None:
"""Called when the application screen size changes."""
window_width = size[0]
if window_width < dp(500):
self.real_device_type = "mobile"
elif window_width < dp(1100):
self.real_device_type = "tablet"
else:
self.real_device_type = "desktop"
def get_real_device_type(self) -> str:
"""Returns the device type - 'mobile', 'tablet' or 'desktop'."""
return self.real_device_type
def get_window_width_resizing_direction(self) -> str:
"""Return window width resizing direction - 'left' or 'right'"""
"""Return window width resizing direction - 'left' or 'right'."""
return self.window_resizing_direction

View File

@ -1,5 +1,4 @@
#:import DEVICE_TYPE kivymd.material_resources.DEVICE_TYPE
#:import FakeRectangularElevationBehavior kivymd.uix.behaviors.FakeRectangularElevationBehavior
<CellRow>
@ -66,7 +65,7 @@
size_hint_y: None
height: self.minimum_height
spacing: "4dp"
tooltip_text: root.text
tooltip_text: root.tooltip if root.tooltip else root.text
BoxLayout:
id: box
@ -175,7 +174,11 @@
font_size: "14sp"
on_release: root.table_data.open_pagination_menu()
text:
f"{root.table_data.rows_num if root.table_data.rows_num < len(root.table_data.row_data) else len(root.table_data.row_data)}"
"{}".format( \
root.table_data.rows_num \
if root.table_data.rows_num < len(root.table_data.row_data) else \
len(root.table_data.row_data) \
)
Widget:
size_hint_x: None
@ -192,9 +195,11 @@
if root.theme_cls.theme_style == "Dark" else \
(0, 0, 0, 1)
text:
f"1-" \
f"{root.table_data.rows_num if root.table_data.rows_num > len(root.table_data.row_data) else len(root.table_data.row_data)} " \
f"of {len(root.table_data.row_data)}"
"1-{} of {}".format( \
root.table_data.rows_num \
if root.table_data.rows_num > len(root.table_data.row_data) else \
len(root.table_data.row_data), len(root.table_data.row_data) \
)
MDIconButton:
id: button_back
@ -217,7 +222,7 @@
on_release: root.table_data.set_next_row_data_parts("forward")
<TableContainer@MDCard+FakeRectangularElevationBehavior>
<TableContainer@MDCard>
<MDDataTable>

View File

@ -11,19 +11,6 @@ Components/DataTables
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-previous.png
:align: center
Warnings
---------
.. warning:: Data tables are still far from perfect. The class is in constant
change, because of optimizations and bug fixes. If you find a bug or have
an improvement you want to share, take some time and share your discoveries
with us over the main git repo.
Any help is well appreciated.
.. warning:: In versions prior to `Kivy 2.1.0-dev0` exists an error in which is
the table has only one row in the current page, the table will only render
one column instead of the whole row.
.. note:: `MDDataTable` allows developers to sort the data provided by column.
This happens thanks to the use of an external function that you can bind
while you're defining the table columns. Be aware that the sorting function
@ -159,6 +146,15 @@ class CellHeader(MDTooltip, BoxLayout):
and defaults to `''`.
"""
tooltip = StringProperty()
"""
Tooltip containing descriptive text for the column.
If the tooltip is not provided, column `text` shall be used instead.
:attr:`tooltip` is a :class:`~kivy.properties.StringProperty`
and defaults to `''`.
"""
# TODO: Added example.
sort_action = ObjectProperty()
"""
@ -340,11 +336,19 @@ class TableHeader(ThemableBehavior, ScrollView):
CellHeader(
text=col_heading[0],
sort_action=col_heading[2],
tooltip=col_heading[3],
width=self.cols_minimum[i],
table_data=self.table_data,
is_sorted=(col_heading[0] == self.sorted_on),
sorted_order=self.sorted_order,
)
if len(col_heading) == 4
else CellHeader(
text=col_heading[0],
sort_action=col_heading[2],
width=self.cols_minimum[i],
table_data=self.table_data,
)
if len(col_heading) == 3
else CellHeader(
text=col_heading[0],
@ -356,6 +360,9 @@ class TableHeader(ThemableBehavior, ScrollView):
else:
# Sets the text in the first cell.
self.ids.first_cell.text = col_heading[0]
self.ids.first_cell.tooltip = (
col_heading[3] if len(col_heading) == 4 else ""
)
self.ids.first_cell.ids.separator.height = 0
self.ids.first_cell.width = self.cols_minimum[i]
@ -765,6 +772,9 @@ class TablePagination(ThemableBehavior, MDBoxLayout):
class MDDataTable(ThemableBehavior, AnchorLayout):
"""
See :class:`~kivy.uix.anchorlayout.AnchorLayout` class documentation for
more information.
:Events:
:attr:`on_row_press`
Called when a table row is clicked.
@ -775,7 +785,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
.. code-block:: python
from kivy.metrics import dp
from kivymd.app import MDApp
@ -914,6 +923,10 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
"""
Data for header columns.
.. tabs::
.. tab:: Imperative python style
.. code-block:: python
from kivy.metrics import dp
@ -925,14 +938,17 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
self.data_tables = MDDataTable(
size_hint=(0.7, 0.6),
use_pagination=True,
check=True,
# name column, width column, sorting function column(optional)
# name column, width column, sorting function column(optional), custom tooltip
column_data=[
("No.", dp(30)),
("No.", dp(30), None, "Custom tooltip"),
("Status", dp(30)),
("Signal Name", dp(60)),
("Severity", dp(30)),
@ -947,6 +963,43 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
Example().run()
.. tab:: Declarative python style
.. code-block:: python
from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.anchorlayout import MDAnchorLayout
from kivymd.uix.datatables import MDDataTable
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return MDAnchorLayout(
MDDataTable(
size_hint=(0.7, 0.6),
use_pagination=True,
check=True,
# name column, width column, sorting function column(optional)
column_data=[
("No.", dp(30)),
("Status", dp(30)),
("Signal Name", dp(60)),
("Severity", dp(30)),
("Stage", dp(30)),
("Schedule", dp(30),
lambda *args: print("Sorted using Schedule")),
("Team Lead", dp(30)),
],
)
)
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-column-data.png
:align: center
@ -1060,6 +1113,9 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
data_tables = MDDataTable(
size_hint=(0.9, 0.6),
@ -1187,7 +1243,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
"""
Use or not use checkboxes for rows.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-check.gif
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-check.png
:align: center
:attr:`check` is an :class:`~kivy.properties.BooleanProperty`
@ -1209,6 +1265,9 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
data_tables = MDDataTable(
size_hint=(0.9, 0.6),
@ -1238,19 +1297,19 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
and defaults to `False`.
"""
elevation = NumericProperty(8)
elevation = NumericProperty(4)
"""
Table elevation.
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to `8`.
and defaults to `4`.
"""
rows_num = NumericProperty(5)
"""
The number of rows displayed on one page of the table.
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-use-pagination.gif
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-use-pagination-rows-num.png
:align: center
:attr:`rows_num` is an :class:`~kivy.properties.NumericProperty`
@ -1266,7 +1325,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
.. rubric:: Center
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-pos-center.png
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-pos-top.png
:align: center
.. rubric:: Auto
@ -1282,11 +1341,6 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
"""
Menu height for selecting the number of displayed rows.
.. rubric:: 140dp
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-height-140.png
:align: center
.. rubric:: 240dp
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-menu-height-240.png
@ -1298,7 +1352,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color = ColorProperty([0, 0, 0, 0])
"""
Background color in the format (r, g, b, a).
Background color in the format (r, g, b, a) or string format.
See :attr:`~kivy.uix.modalview.ModalView.background_color`.
Use markup strings
@ -1315,6 +1369,9 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
class Example(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = AnchorLayout()
data_tables = MDDataTable(
size_hint=(0.9, 0.6),
@ -1354,7 +1411,8 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_header = ColorProperty(None)
"""
Background color for :class:`~TableHeader` class.
Background color in the format (r, g, b, a) or string format for
:class:`~TableHeader` class.
.. versionadded:: 1.0.0
@ -1374,7 +1432,8 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_cell = ColorProperty(None)
"""
Background color for :class:`~CellRow` class.
Background color in the format (r, g, b, a) or string format for
:class:`~CellRow` class.
.. versionadded:: 1.0.0
@ -1395,7 +1454,8 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_selected_cell = ColorProperty(None)
"""
Background selected color for :class:`~CellRow` class.
Background selected color in the format (r, g, b, a) or string format for
:class:`~CellRow` class.
.. versionadded:: 1.0.0
@ -1408,7 +1468,7 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
background_color_selected_cell="e4514f",
)
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-background-color-selected-cell.gif
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/data-tables-background-color-selected-cell.png
:align: center
:attr:`background_color_selected_cell` is a :class:`~kivy.properties.ColorProperty` and
@ -1503,6 +1563,9 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
data_tables = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = MDFloatLayout() # root layout
# Creating control buttons.
button_box = MDBoxLayout(
@ -1604,6 +1667,9 @@ class MDDataTable(ThemableBehavior, AnchorLayout):
data_tables = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
layout = MDFloatLayout()
layout.add_widget(
MDRaisedButton(

View File

@ -18,7 +18,11 @@
PopMatrix
<DialogContainer@MDCard+FakeRectangularElevationBehavior>
<DialogContainer@MDCard>
shadow_color: 0.0, 0.0, 0.0, 0.0
elevation: 0
shadow_softness: 0
shadow_offset: 0, 0
<MDDialog>
@ -28,7 +32,6 @@
orientation: "vertical"
size_hint_y: None
height: self.minimum_height
elevation: 24
padding: "24dp", "24dp", "8dp", "8dp"
radius: root.radius
md_bg_color:

View File

@ -38,6 +38,8 @@ Usage
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_alert_dialog(self):
@ -87,6 +89,7 @@ from kivy.uix.modalview import ModalView
from kivymd import uix_path
from kivymd.material_resources import DEVICE_TYPE
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import CommonElevationBehavior
from kivymd.uix.button import BaseButton
from kivymd.uix.card import MDSeparator
from kivymd.uix.list import BaseListItem
@ -97,7 +100,40 @@ with open(
Builder.load_string(kv_file.read())
class BaseDialog(ThemableBehavior, ModalView):
class BaseDialog(ThemableBehavior, ModalView, CommonElevationBehavior):
elevation = NumericProperty(3)
"""
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.elevation`
attribute for more information.
.. versionadded:: 1.1.0
:attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
and defaults to `3`.
"""
shadow_softness = NumericProperty(24)
"""
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_softness`
attribute for more information.
.. versionadded:: 1.1.0
:attr:`shadow_softness` is an :class:`~kivy.properties.NumericProperty`
and defaults to `24`.
"""
shadow_offset = ListProperty((0, 4))
"""
See :attr:`kivymd.uix.behaviors.elevation.CommonElevationBehavior.shadow_offset`
attribute for more information.
.. versionadded:: 1.1.0
:attr:`shadow_offset` is an :class:`~kivy.properties.ListProperty`
and defaults to `[0, 4]`.
"""
radius = ListProperty([dp(7), dp(7), dp(7), dp(7)])
"""
Dialog corners rounding value.
@ -251,6 +287,8 @@ class MDDialog(BaseDialog):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_simple_dialog(self):
@ -259,9 +297,8 @@ class MDDialog(BaseDialog):
title="Set backup account",
type="simple",
items=[
Item(text="user01@gmail.com", source="user-1.png"),
Item(text="user02@gmail.com", source="user-2.png"),
Item(text="Add account", source="add-icon.png"),
Item(text="user01@gmail.com", source="kivymd/images/logo/kivymd-icon-128.png"),
Item(text="user02@gmail.com", source="data/logo/kivy-icon-128.png"),
],
)
self.dialog.open()
@ -317,6 +354,8 @@ class MDDialog(BaseDialog):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_confirmation_dialog(self):
@ -385,6 +424,10 @@ class MDDialog(BaseDialog):
"""
Custom content class.
.. tabs::
.. tab:: Declarative KV style
.. code-block:: python
from kivy.lang import Builder
@ -425,6 +468,8 @@ class MDDialog(BaseDialog):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return Builder.load_string(KV)
def show_confirmation_dialog(self):
@ -451,6 +496,69 @@ class MDDialog(BaseDialog):
Example().run()
.. tab:: Declarative Python style
.. code-block:: python
from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.textfield import MDTextField
class Example(MDApp):
dialog = None
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Orange"
return (
MDFloatLayout(
MDFlatButton(
text="ALERT DIALOG",
pos_hint={'center_x': 0.5, 'center_y': 0.5},
on_release=self.show_confirmation_dialog,
)
)
)
def show_confirmation_dialog(self, *args):
if not self.dialog:
self.dialog = MDDialog(
title="Address:",
type="custom",
content_cls=MDBoxLayout(
MDTextField(
hint_text="City",
),
MDTextField(
hint_text="Street",
),
orientation="vertical",
spacing="12dp",
size_hint_y=None,
height="120dp",
),
buttons=[
MDFlatButton(
text="CANCEL",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
MDFlatButton(
text="OK",
theme_text_color="Custom",
text_color=self.theme_cls.primary_color,
),
],
)
self.dialog.open()
Example().run()
.. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/dialog-custom.png
:align: center
@ -460,7 +568,7 @@ class MDDialog(BaseDialog):
md_bg_color = ColorProperty(None)
"""
Background color in the format (r, g, b, a).
Background color in the (r, g, b, a) or string format.
:attr:`md_bg_color` is an :class:`~kivy.properties.ColorProperty`
and defaults to `None`.

View File

@ -1,7 +1,7 @@
<_Triangle>:
canvas:
Color:
rgba: root.theme_cls.text_color
rgba: app.theme_cls.text_color
Triangle:
points:
[ \
@ -13,7 +13,8 @@
<MDDropDownItem>
orientation: "vertical"
adaptive_size: True
size_hint: None, None
size: self.minimum_size
spacing: "5dp"
padding: "5dp", "5dp", "5dp", 0

View File

@ -15,13 +15,13 @@ Usage
from kivymd.app import MDApp
KV = '''
Screen
MDScreen
MDDropDownItem:
id: drop_item
pos_hint: {'center_x': .5, 'center_y': .5}
text: 'Item'
on_release: self.set_item("New Item")
on_release: print("Press item")
'''
@ -48,12 +48,12 @@ import os
from kivy.lang import Builder
from kivy.properties import NumericProperty, StringProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivymd import uix_path
from kivymd.theming import ThemableBehavior
from kivymd.uix.behaviors import FakeRectangularElevationBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.behaviors import DeclarativeBehavior
with open(
os.path.join(uix_path, "dropdownitem", "dropdownitem.kv"), encoding="utf-8"
@ -61,15 +61,12 @@ with open(
Builder.load_string(kv_file.read())
class _Triangle(ThemableBehavior, Widget):
class _Triangle(Widget):
pass
class MDDropDownItem(
ThemableBehavior,
FakeRectangularElevationBehavior,
ButtonBehavior,
MDBoxLayout,
DeclarativeBehavior, ThemableBehavior, ButtonBehavior, BoxLayout
):
text = StringProperty()
"""

View File

@ -56,6 +56,8 @@ Example
.. code-block:: python
import os
from kivy.lang import Builder
from kivymd.app import MDApp
@ -75,7 +77,7 @@ Example
icon: 'phone'
ScrollView:
MDScrollView:
MDGridLayout:
id: box
@ -96,7 +98,7 @@ Example
for i in range(10):
self.root.ids.box.add_widget(
MDExpansionPanel(
icon=f"{images_path}kivymd.png",
icon=os.path.join(images_path, "logo", "kivymd-icon-128.png"),
content=Content(),
panel_cls=MDExpansionPanelThreeLine(
text="Text",

View File

@ -6,28 +6,29 @@
background_normal: ""
background_down: ""
dir_or_file_name: ""
icon_color: 0, 0, 0, 0
_selected: False
events_callback: lambda x: None
orientation: "vertical"
ModifiedOneLineIconListItem:
text: root.dir_or_file_name
on_release: root.events_callback(root.path, root)
bg_color:
self.theme_cls.bg_darkest \
if root._selected else self.theme_cls.bg_normal
on_release: root.events_callback(root.path, root)
if root._selected else \
self.theme_cls.bg_normal
IconLeftWidget:
icon: root.icon
theme_text_color: "Custom"
text_color: self.theme_cls.primary_color
theme_icon_color: "Custom"
icon_color: root.icon_color
MDSeparator:
<LabelContent@MDLabel>
size_hint_y: None
height: self.texture_size[1]
adaptive_height: True
shorten: True
shorten_from: "center"
halign: "center"
@ -61,23 +62,6 @@
text: root.name
<FloatButton>
anchor_x: "right"
anchor_y: "bottom"
size_hint_y: None
height: dp(56)
padding: dp(10)
MDFloatingActionButton:
size_hint: None, None
size:dp(56), dp(56)
icon: root.icon
opposite_colors: True
elevation: 8
on_release: root.callback()
md_bg_color: root.md_bg_color
<MDFileManager>
md_bg_color: root.theme_cls.bg_normal
@ -90,7 +74,11 @@
title: root.current_path
right_action_items: [["close-box", lambda x: root.exit_manager(1)]]
left_action_items: [["chevron-left", lambda x: root.back()]]
elevation: 10
elevation: 3
md_bg_color:
app.theme_cls.primary_color \
if not root.background_color_toolbar else \
root.background_color_toolbar
RecycleView:
id: rv

Some files were not shown because too many files have changed in this diff Show More