├── .ci ├── macos_dependencies.sh ├── macos_versions.sh ├── move_binary.py ├── ubuntu_dependencies.sh └── windows_dependencies.ps1 ├── .github └── workflows │ ├── build.yml │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yml ├── LICENSE ├── README.md ├── docs ├── Makefile ├── make.bat └── sources │ ├── .gitignore │ ├── 404.rst │ ├── _extensions │ ├── __init__.py │ ├── autoapi_kivymd.py │ ├── kivy_lexer.py │ └── toctree_with_sort.py │ ├── _static │ └── logo-kivymd.png │ ├── _templates │ └── python │ │ └── module.rst │ ├── about.rst │ ├── changelog │ ├── 0.100.0.rst │ ├── 0.100.1.rst │ ├── 0.100.2.rst │ ├── 0.101.0.rst │ ├── 0.101.1.rst │ ├── 0.101.2.rst │ ├── 0.101.3.rst │ ├── 0.101.4.rst │ ├── 0.101.5.rst │ ├── 0.101.6.rst │ ├── 0.101.7.rst │ ├── 0.101.8.rst │ ├── 0.102.0.rst │ ├── 0.102.1.rst │ ├── 0.103.0.rst │ ├── 0.104.0.rst │ ├── 0.104.1.rst │ ├── 0.104.2.rst │ ├── 0.99.92.rst │ ├── 0.99.93.rst │ ├── 0.99.94.rst │ ├── 0.99.95.rst │ ├── 0.99.96.rst │ ├── 0.99.97.rst │ ├── 0.99.98.rst │ ├── 0.99.99.01.rst │ ├── 0.99.99.rst │ ├── 1.0.0.rst │ ├── 1.0.1.rst │ ├── 1.0.2.rst │ ├── 1.1.1.rst │ ├── 1.2.0.rst │ ├── index.rst │ └── unreleased.rst │ ├── conf.py │ ├── getting-started.rst │ ├── index.rst │ └── themes │ └── index.rst ├── examples ├── appbar.py ├── avatar.png ├── badge.py ├── bg.jpg ├── bottomappbar.py ├── bottomsheet.py ├── button.py ├── card.py ├── checkbox.py ├── chip.py ├── common_app.py ├── datepicker.py ├── dialog.py ├── dropdownitem.py ├── dynamic_color_image.py ├── dynamic_color_schemes.py ├── expansionpanel.py ├── hero.py ├── imagelist.py ├── label.py ├── list.py ├── material_scroll.py ├── md_axis_transition.py ├── md_transitions.py ├── navigation_bar.py ├── navigation_rail.py ├── navigationdrawer.py ├── progressindicator.py ├── segmented_button.py ├── slider.py ├── snackbar.py ├── switch.py ├── tab.py ├── textfield.py ├── timepicker.py └── tooltip.py ├── kivymd ├── __init__.py ├── _version.py ├── animation.py ├── app.py ├── dynamic_color.py ├── effects │ ├── __init__.py │ └── stiffscroll │ │ ├── LICENSE │ │ ├── README.md │ │ ├── __init__.py │ │ └── stiffscroll.py ├── factory_registers.py ├── font_definitions.py ├── fonts │ ├── LICENSE.txt │ ├── Roboto-Black.ttf │ ├── Roboto-BlackItalic.ttf │ ├── Roboto-Bold.ttf │ ├── Roboto-BoldItalic.ttf │ ├── Roboto-Italic.ttf │ ├── Roboto-Light.ttf │ ├── Roboto-LightItalic.ttf │ ├── Roboto-Medium.ttf │ ├── Roboto-MediumItalic.ttf │ ├── Roboto-Regular.ttf │ ├── Roboto-Thin.ttf │ ├── Roboto-ThinItalic.ttf │ └── materialdesignicons-webfont.ttf ├── icon_definitions.py ├── images │ ├── folder.png │ ├── logo │ │ ├── kivymd-icon-128.png │ │ ├── kivymd-icon-256.png │ │ └── kivymd-icon-512.png │ └── transparent.png ├── material_resources.py ├── tests │ ├── button │ │ ├── test_bg_color_after_changed_theme.py │ │ ├── test_fab_size_m3_style.py │ │ ├── test_icon_button_md_bg_color_disabled.py │ │ ├── test_md_icon_button_icon_size.py │ │ ├── test_on_release.py │ │ ├── test_outline_button_custom_color.py │ │ ├── test_outline_button_custom_color_after_disabled.py │ │ └── test_size_hint.py │ ├── data │ │ └── danger.ttf │ ├── hero │ │ └── test_with_first_screen_without_hero.py │ ├── label │ │ ├── test_allow_copy.py │ │ ├── test_color_deselection.py │ │ ├── test_color_selection.py │ │ ├── test_default_theme_text_color.py │ │ ├── test_disabled_color.py │ │ ├── test_font_size_in_python_code.py │ │ ├── test_font_style.py │ │ ├── test_md_icon_badge_colors.py │ │ ├── test_md_icon_badge_colors_after_disabled.py │ │ ├── test_switch_theme.py │ │ └── test_theme_text_color.py │ ├── list │ │ └── test_disable.py │ ├── memory │ │ ├── test_button.py │ │ ├── test_chip.py │ │ ├── test_exdented_fab_button.py │ │ ├── test_fab_button.py │ │ ├── test_icon_button.py │ │ ├── test_label.py │ │ ├── test_slider.py │ │ ├── test_switch.py │ │ └── test_textfield.py │ ├── pyinstaller │ │ └── test_pyinstaller_packaging.py │ ├── test_app.py │ ├── test_font_definitions.py │ ├── test_icon_definitions.py │ ├── test_md_bg_color_layouts.py │ └── textfield │ │ ├── test_disabled_text_color.py │ │ ├── test_disabled_text_color_switch_theme.py │ │ ├── test_error_state.py │ │ ├── test_error_state_color_helper_text_mode_on_focus.py │ │ ├── test_helper_text_mode_on_focus.py │ │ └── test_helper_text_mode_persistent.py ├── theming.py ├── toast │ ├── LICENSE │ ├── __init__.py │ └── androidtoast.py ├── tools │ ├── __init__.py │ ├── argument_parser.py │ ├── hotreload │ │ ├── __init__.py │ │ └── app.py │ ├── packaging │ │ ├── __init__.py │ │ └── pyinstaller │ │ │ ├── __init__.py │ │ │ └── hook-kivymd.py │ ├── patterns │ │ ├── MVC │ │ │ ├── Model │ │ │ │ ├── __init__.py │ │ │ │ ├── database_firebase.py │ │ │ │ └── database_restdb.py │ │ │ ├── __init__.py │ │ │ ├── data │ │ │ │ └── locales │ │ │ │ │ └── po │ │ │ │ │ ├── en.po │ │ │ │ │ └── ru.po │ │ │ ├── libs │ │ │ │ ├── __init__.py │ │ │ │ └── translation.py │ │ │ └── messages.pot │ │ ├── __init__.py │ │ ├── add_view.py │ │ └── create_project.py │ └── release │ │ ├── __init__.py │ │ ├── git_commands.py │ │ ├── make_release.py │ │ └── update_icons.py ├── uix │ ├── __init__.py │ ├── anchorlayout.py │ ├── appbar │ │ ├── __init__.py │ │ ├── appbar.kv │ │ └── appbar.py │ ├── badge │ │ ├── __init__.py │ │ ├── badge.kv │ │ └── badge.py │ ├── behaviors │ │ ├── __init__.py │ │ ├── backgroundcolor_behavior.py │ │ ├── declarative_behavior.py │ │ ├── elevation.py │ │ ├── focus_behavior.py │ │ ├── hover_behavior.py │ │ ├── magic_behavior.py │ │ ├── motion_behavior.py │ │ ├── ripple_behavior.py │ │ ├── rotate_behavior.py │ │ ├── scale_behavior.py │ │ ├── state_layer_behavior.py │ │ ├── stencil_behavior.py │ │ ├── toggle_behavior.py │ │ └── touch_behavior.py │ ├── bottomsheet │ │ ├── __init__.py │ │ ├── bottomsheet.kv │ │ └── bottomsheet.py │ ├── boxlayout.py │ ├── button │ │ ├── __init__.py │ │ ├── button.kv │ │ └── button.py │ ├── card │ │ ├── __init__.py │ │ ├── card.kv │ │ └── card.py │ ├── chip │ │ ├── __init__.py │ │ ├── chip.kv │ │ └── chip.py │ ├── circularlayout.py │ ├── controllers │ │ ├── __init__.py │ │ └── windowcontroller.py │ ├── dialog │ │ ├── __init__.py │ │ ├── dialog.kv │ │ └── dialog.py │ ├── divider │ │ ├── __init__.py │ │ ├── divider.kv │ │ └── divider.py │ ├── dropdownitem │ │ ├── __init__.py │ │ ├── dropdownitem.kv │ │ └── dropdownitem.py │ ├── expansionpanel │ │ ├── __init__.py │ │ ├── expansionpanel.kv │ │ └── expansionpanel.py │ ├── filemanager │ │ ├── __init__.py │ │ ├── filemanager.kv │ │ └── filemanager.py │ ├── fitimage │ │ ├── __init__.py │ │ └── fitimage.py │ ├── floatlayout.py │ ├── gridlayout.py │ ├── hero.py │ ├── imagelist │ │ ├── __init__.py │ │ ├── imagelist.kv │ │ └── imagelist.py │ ├── label │ │ ├── __init__.py │ │ ├── label.kv │ │ └── label.py │ ├── list │ │ ├── __init__.py │ │ ├── list.kv │ │ └── list.py │ ├── menu │ │ ├── __init__.py │ │ ├── menu.kv │ │ └── menu.py │ ├── navigationbar │ │ ├── __init__.py │ │ ├── navigationbar.kv │ │ └── navigationbar.py │ ├── navigationdrawer │ │ ├── __init__.py │ │ ├── navigationdrawer.kv │ │ └── navigationdrawer.py │ ├── navigationrail │ │ ├── __init__.py │ │ ├── navigationrail.kv │ │ └── navigationrail.py │ ├── pickers │ │ ├── __init__.py │ │ ├── datepicker │ │ │ ├── __init__.py │ │ │ ├── datepicker.kv │ │ │ └── datepicker.py │ │ └── timepicker │ │ │ ├── __init__.py │ │ │ ├── timepicker.kv │ │ │ └── timepicker.py │ ├── progressindicator │ │ ├── __init__.py │ │ ├── progressindicator.kv │ │ └── progressindicator.py │ ├── recycleboxlayout.py │ ├── recyclegridlayout.py │ ├── recycleview.py │ ├── refreshlayout │ │ ├── __init__.py │ │ ├── refreshlayout.kv │ │ └── refreshlayout.py │ ├── relativelayout.py │ ├── responsivelayout.py │ ├── screen.py │ ├── screenmanager.py │ ├── scrollview.py │ ├── segmentedbutton │ │ ├── __init__.py │ │ ├── segmentedbutton.kv │ │ └── segmentedbutton.py │ ├── selectioncontrol │ │ ├── __init__.py │ │ ├── selectioncontrol.kv │ │ └── selectioncontrol.py │ ├── slider │ │ ├── __init__.py │ │ ├── slider.kv │ │ └── slider.py │ ├── sliverappbar │ │ ├── __init__.py │ │ ├── sliverappbar.kv │ │ └── sliverappbar.py │ ├── snackbar │ │ ├── __init__.py │ │ ├── snackbar.kv │ │ └── snackbar.py │ ├── stacklayout.py │ ├── swiper │ │ ├── __init__.py │ │ ├── swiper.kv │ │ └── swiper.py │ ├── tab │ │ ├── __init__.py │ │ ├── tab.kv │ │ └── tab.py │ ├── textfield │ │ ├── __init__.py │ │ ├── textfield.kv │ │ └── textfield.py │ ├── tooltip │ │ ├── __init__.py │ │ ├── tooltip.kv │ │ └── tooltip.py │ ├── transition │ │ ├── __init__.py │ │ └── transition.py │ └── widget.py └── utils │ ├── __init__.py │ ├── fpsmonitor.py │ └── set_bars_colors.py ├── pyproject.toml ├── setup.cfg └── setup.py /.ci/macos_versions.sh: -------------------------------------------------------------------------------- 1 | # See https://github.com/kivy/kivy/blob/master/.ci/osx_versions.sh 2 | # Do not use cache if versions are different 3 | 4 | export ARIAL2=1.35.0 5 | export SDL2=2.0.12 6 | export SDL2_IMAGE=2.0.5 7 | export SDL2_MIXER=2.0.4 8 | export SDL2_TTF=2.0.15 9 | export GSTREAMER=1.16.2 10 | export PLATYPUS=5.3 11 | -------------------------------------------------------------------------------- /.ci/ubuntu_dependencies.sh: -------------------------------------------------------------------------------- 1 | # See https://github.com/kivy/kivy/blob/master/.github/workflows/test_ubuntu_python.yml 2 | 3 | # Environment variables 4 | echo "DISPLAY=:99.0" >> $GITHUB_ENV 5 | 6 | # System dependencies 7 | sudo apt-get update 8 | sudo apt-get -y install \ 9 | libsdl2-dev libsdl2-ttf-dev libsdl2-image-dev libsdl2-mixer-dev \ 10 | libgstreamer1.0-dev gstreamer1.0-alsa gstreamer1.0-plugins-base \ 11 | libsmpeg-dev libswscale-dev libavformat-dev libavcodec-dev libjpeg-dev libtiff5-dev libx11-dev libmtdev-dev \ 12 | build-essential libgl1-mesa-dev libgles2-mesa-dev \ 13 | xvfb pulseaudio xsel 14 | 15 | # Pip dependencies 16 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 17 | python get-pip.py --user 18 | 19 | python -m pip install --upgrade pip setuptools wheel 20 | python -m pip install --upgrade \ 21 | cython \ 22 | pytest pytest-cov pytest_asyncio pytest-timeout coveralls \ 23 | pillow docutils pygments pyinstaller[hook_testing] \ 24 | sphinx sphinxcontrib-blockdiag sphinxcontrib-seqdiag sphinxcontrib-actdiag sphinxcontrib-nwdiag 25 | python -m pip install --upgrade kivy==$KIVY_VERSION 26 | 27 | # Start X-Server 28 | /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background \ 29 | --exec /usr/bin/Xvfb -- :99 -screen 0 1280x720x24 -ac +extension GLX 30 | -------------------------------------------------------------------------------- /.ci/windows_dependencies.ps1: -------------------------------------------------------------------------------- 1 | # See https://github.com/kivy/kivy/blob/master/.github/workflows/test_windows_python.yml 2 | 3 | # Environment variables 4 | echo "GST_REGISTRY=~/registry.bin" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 5 | echo "KIVY_GL_BACKEND=angle_sdl2" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append 6 | 7 | # Pip dependencies 8 | python -m pip install --upgrade pip setuptools wheel 9 | python -m pip install --upgrade ` 10 | cython ` 11 | pytest pytest-cov pytest_asyncio pytest-timeout coveralls ` 12 | pillow pyinstaller[hook_testing] ` 13 | pypiwin32 kivy_deps.sdl2 kivy_deps.glew kivy_deps.angle kivy_deps.gstreamer 14 | python -m pip install --upgrade kivy==$Env:KIVY_VERSION 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - data 7 | - gh-pages 8 | tags: 9 | - '**' 10 | pull_request: 11 | branches-ignore: 12 | - data 13 | - gh-pages 14 | workflow_dispatch: 15 | 16 | jobs: 17 | build: 18 | name: Build [${{ matrix.python-version }} | ${{ matrix.os }}] 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | os: [ubuntu-latest] 24 | python-version: ['3.13'] 25 | steps: 26 | - name: Checkout source 27 | uses: actions/checkout@v4 28 | 29 | - name: Set up Python ${{ matrix.python-version }} 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: ${{ matrix.python-version }} 33 | 34 | - name: Install build tools 35 | run: | 36 | python -m pip install --upgrade pip 37 | pip install build 38 | 39 | - name: Build with `python -m build` 40 | run: | 41 | python -m build --wheel --sdist 42 | 43 | - name: Upload merged artifacts per OS 44 | uses: actions/upload-artifact@v4 45 | with: 46 | name: dist-${{ matrix.os }} 47 | path: dist/ 48 | if-no-files-found: error 49 | 50 | deploy: 51 | name: Publish to PyPI 52 | runs-on: ubuntu-latest 53 | needs: build 54 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 55 | 56 | steps: 57 | - name: Checkout source 58 | uses: actions/checkout@v4 59 | 60 | - name: Download all artifacts 61 | uses: actions/download-artifact@v4 62 | with: 63 | path: dist 64 | 65 | - name: Publish to PyPI 66 | uses: pypa/gh-action-pypi-publish@v1.8.11 67 | with: 68 | user: __token__ 69 | password: ${{ secrets.PYPI_TOKEN }} 70 | packages_dir: dist/ 71 | 72 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches-ignore: 5 | - data 6 | - gh-pages 7 | tags: 8 | - '**' 9 | pull_request: 10 | branches-ignore: 11 | - data 12 | - gh-pages 13 | workflow_dispatch: 14 | 15 | jobs: 16 | 17 | # Lint job. Runs pre-commit, fails if there are changed files 18 | # os, python version and architecture does not matter here 19 | # [${{ matrix.python-version }} | ${{ matrix.os }} ${{ matrix.architecture }}] 20 | lint: 21 | name: Check pre-commit 22 | strategy: 23 | fail-fast: true 24 | matrix: 25 | os: [ubuntu-latest] 26 | python-version: ['3.13'] 27 | architecture: [x64] 28 | runs-on: ${{ matrix.os }} 29 | env: 30 | PYTHONUNBUFFERED: 1 31 | 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | 36 | - name: Set up Python ${{ matrix.python-version }} 37 | uses: actions/setup-python@v2 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | architecture: ${{ matrix.architecture }} 41 | 42 | - name: Install dependencies 43 | run: | 44 | python -m pip install --upgrade pip 45 | python -m pip install --upgrade setuptools wheel 46 | python -m pip install --upgrade pre-commit 47 | 48 | - name: Check pre-commit 49 | run: pre-commit run --all-files 50 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches-ignore: 5 | - data 6 | - gh-pages 7 | tags: 8 | - '**' 9 | pull_request: 10 | branches-ignore: 11 | - data 12 | - gh-pages 13 | workflow_dispatch: 14 | inputs: 15 | version: 16 | description: New version in format n.n.n (1.111.11) 17 | required: true 18 | next_version: 19 | description: Next development version in format n.n.n.devn (1.111.11.dev0). USE ONLY FOR MAJOR DEVELOPMENT VERSIONS (2.0.0.dev0). DON'T WRITE ANYTHING HERE 20 | required: false 21 | 22 | jobs: 23 | 24 | # Release job. Runs make_release.py tool. Pushes changes only on dispatch_event 25 | release: 26 | name: Release 27 | runs-on: ubuntu-latest 28 | env: 29 | PYTHONUNBUFFERED: 1 30 | 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v2 34 | with: 35 | fetch-depth: 0 36 | persist-credentials: ${{ github.event_name != 'workflow_dispatch' }} # Allow using PAT when making release 37 | 38 | - name: Set up Python 3.13 39 | uses: actions/setup-python@v5 40 | with: 41 | python-version: '3.13' 42 | architecture: x64 43 | 44 | - name: Set up environment 45 | run: | 46 | git config user.name "KivyMD Bot" 47 | git config user.email 69076719+KivyMD-Bot@users.noreply.github.com 48 | pip install -e . 49 | pip install pre-commit 50 | 51 | - name: Release 52 | if: github.event_name == 'workflow_dispatch' 53 | run: | 54 | # Use personal token to push (when using GITHUB_TOKEN, it will not run workflows) 55 | git remote set-url origin https://KivyMD-Bot:${{ secrets.GH_PAT }}@github.com/${{ github.repository }} 56 | python kivymd/tools/release/make_release.py release "${{ github.event.inputs.version }}" "${{ github.event.inputs.next_version }}" --yes --push 57 | 58 | - name: Release preparation 59 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.event_name != 'workflow_dispatch' 60 | run: | 61 | # Use personal token to push 62 | git remote set-url origin https://KivyMD-Bot:${{ secrets.GH_PAT }}@github.com/${{ github.repository }} 63 | python kivymd/tools/release/make_release.py prepare --yes --push 64 | git format-patch origin/master... --stdout 65 | 66 | - name: Release test 67 | if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/master' && github.event_name != 'workflow_dispatch') 68 | run: | 69 | python kivymd/tools/release/make_release.py test --yes 70 | git format-patch origin/master... --stdout 71 | git format-patch origin/master... --stdout > release_test.patch 72 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches-ignore: 5 | - data 6 | - gh-pages 7 | tags: 8 | - '**' 9 | pull_request: 10 | branches-ignore: 11 | - data 12 | - gh-pages 13 | workflow_dispatch: 14 | 15 | jobs: 16 | 17 | # Test job. Runs pytest on Ubuntu, MacOS and Windows, uploads coverage report 18 | test: 19 | name: Test [${{ matrix.python-version }} | Kivy ${{ matrix.kivy-version }} | ${{ matrix.os }} ${{ matrix.architecture }}] 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | os: [ubuntu-latest, macos-latest, windows-latest] 24 | python-version: ['3.13'] 25 | architecture: [x64] 26 | kivy-version: [2.3.1] 27 | runs-on: ${{ matrix.os }} 28 | env: 29 | PYTHONUNBUFFERED: 1 30 | 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Set up Python ${{ matrix.python-version }} 36 | uses: actions/setup-python@v5 37 | with: 38 | python-version: ${{ matrix.python-version }} 39 | architecture: ${{ matrix.architecture }} 40 | 41 | - name: Cache MacOS deps 42 | uses: actions/cache@v4 43 | if: startswith(matrix.os, 'macos') 44 | with: 45 | path: macos-cache 46 | key: ${{ runner.OS }}-deps-${{ hashFiles('.ci/macos_versions.sh') }} 47 | 48 | - name: Cache MacOS gst-devel deps 49 | uses: actions/cache@v4 50 | if: startswith(matrix.os, 'macos') 51 | with: 52 | path: macos-cache-gst-devel 53 | key: gst-devel-${{ runner.OS }}-deps-gst-devel-${{ hashFiles('.ci/macos_versions.sh') }} 54 | 55 | - name: Install dependencies for Ubuntu 56 | if: startswith(matrix.os, 'ubuntu') 57 | env: 58 | KIVY_VERSION: ${{ matrix.kivy-version }} 59 | run: .ci/ubuntu_dependencies.sh 60 | 61 | - name: Install dependencies for MacOS 62 | if: startswith(matrix.os, 'macos') 63 | env: 64 | KIVY_VERSION: ${{ matrix.kivy-version }} 65 | run: .ci/macos_dependencies.sh 66 | 67 | - name: Install dependencies for Windows 68 | if: startswith(matrix.os, 'windows') 69 | env: 70 | KIVY_VERSION: ${{ matrix.kivy-version }} 71 | run: .ci\windows_dependencies.ps1 72 | 73 | - name: Install KivyMD 74 | run: python -m pip install -e . 75 | 76 | - name: Test 77 | run: python -m pytest kivymd/tests --timeout=300 --cov=kivymd --cov-report=term 78 | 79 | - name: Test packaging with PyInstaller 80 | run: python -m PyInstaller.utils.run_tests --include_only kivymd. 81 | 82 | # Only from Ubuntu 83 | - name: Upload coverage report 84 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == 'kivymd/KivyMD' && startswith(matrix.os, 'ubuntu') 85 | continue-on-error: true 86 | env: 87 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 88 | run: python -m coveralls 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Directories and files that cannot be commited to git 2 | 3 | # Byte-compiled 4 | __pycache__ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | lib 14 | lib64 15 | parts 16 | sdist 17 | var 18 | wheels 19 | pip-wheel-metadata 20 | share/python-wheels 21 | develop-eggs 22 | eggs 23 | .eggs 24 | *.egg-info 25 | *.egg 26 | MANIFEST 27 | .installed.cfg 28 | downloads 29 | docs/_build 30 | build 31 | dist 32 | bin 33 | .buildozer 34 | 35 | # Logs 36 | *.log 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Editors 41 | .vscode 42 | .ipynb_checkpoints 43 | *.swp 44 | # PyCharm 45 | .idea/* 46 | !/.idea/*.iml 47 | !/.idea/modules.xml 48 | !/.idea/vcs.xml 49 | !/.idea/runConfigurations 50 | 51 | # Environments 52 | venv 53 | .venv 54 | env 55 | .env 56 | .python-version 57 | 58 | # Temp / Cache 59 | cache 60 | .cache 61 | temp 62 | .temp 63 | .pytest_cache 64 | .coverage 65 | .DS_Store 66 | .dccache 67 | 68 | /kivymd/tools/release/*.zip 69 | /kivymd/tools/release/temp 70 | .idea/ 71 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Pre-commit hooks. 2 | # python -m pip install pre-commit 3 | # pre-commit install 4 | # pre-commit run --all-files 5 | 6 | repos: 7 | 8 | # Format Python 9 | - repo: https://github.com/psf/black 10 | rev: 23.12.1 11 | hooks: 12 | - id: black 13 | 14 | # Sort imports 15 | - repo: https://github.com/PyCQA/isort 16 | rev: 6.0.1 17 | hooks: 18 | - id: isort 19 | additional_dependencies: ["toml"] 20 | 21 | # Lint Python 22 | - repo: https://github.com/PyCQA/flake8 23 | rev: 7.2.0 24 | hooks: 25 | - id: flake8 26 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-20.04 11 | tools: 12 | python: "3.10" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/sources/conf.py 17 | builder: dirhtml 18 | 19 | # Optionally build your docs in additional formats such as PDF and ePub 20 | formats: all 21 | 22 | # Optionally set the version of Python and requirements required to build your docs 23 | python: 24 | install: 25 | - method: pip 26 | path: . 27 | extra_requirements: 28 | - docs 29 | 30 | search: 31 | ranking: 32 | # Components are more important 33 | components/*: 2 34 | 35 | # Unincluded API isn't very important 36 | api/*: -2 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Andrés Rodríguez and other contributors - KivyMD library up to version 0.1.2 4 | Copyright (c) 2024 KivyMD Team and other contributors - KivyMD library version 0.1.3 and higher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= -T 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = sources 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=sources 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% -b dirhtml %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/sources/.gitignore: -------------------------------------------------------------------------------- 1 | # Directories and files that cannot be commited to git 2 | 3 | /api 4 | -------------------------------------------------------------------------------- /docs/sources/404.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Page not found 4 | ============== 5 | 6 | We could not find this page. 7 | 8 | Try using the search box or links from the sidebar to find what you are looking for. 9 | 10 | Please, `post an issue `_, so we can make redirects to the new page. 11 | -------------------------------------------------------------------------------- /docs/sources/_extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/docs/sources/_extensions/__init__.py -------------------------------------------------------------------------------- /docs/sources/_extensions/kivy_lexer.py: -------------------------------------------------------------------------------- 1 | from kivy.extras.highlight import KivyLexer 2 | 3 | 4 | def setup(app): 5 | app.add_lexer("kv", KivyLexer) 6 | -------------------------------------------------------------------------------- /docs/sources/_extensions/toctree_with_sort.py: -------------------------------------------------------------------------------- 1 | from docutils.parsers.rst import directives 2 | from sphinx.directives.other import TocTree, int_or_nothing 3 | 4 | 5 | class TocTreeWithSort(TocTree): 6 | option_spec = { 7 | "maxdepth": int, 8 | "name": directives.unchanged, 9 | "caption": directives.unchanged_required, 10 | "glob": directives.flag, 11 | "hidden": directives.flag, 12 | "includehidden": directives.flag, 13 | "numbered": int_or_nothing, 14 | "titlesonly": directives.flag, 15 | "reversed": directives.flag, 16 | "sorted": directives.flag, 17 | } 18 | 19 | def parse_content(self, toctree): 20 | ret = super().parse_content(toctree) 21 | if "sorted" in self.options: 22 | toctree["entries"] = sorted(toctree["entries"]) 23 | return ret 24 | 25 | 26 | def setup(app): 27 | directives.register_directive("toctree", TocTreeWithSort) 28 | -------------------------------------------------------------------------------- /docs/sources/_static/logo-kivymd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/docs/sources/_static/logo-kivymd.png -------------------------------------------------------------------------------- /docs/sources/_templates/python/module.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/kivymd/KivyMD/blob/master/{{ obj.obj.relative_path|replace("\\", "/") }} 2 | {% if not obj.display %} 3 | {# Do not display warnings #} 4 | :orphan: 5 | {% endif %} 6 | 7 | {# Write last word in summary #} 8 | {% set unincluded = obj.include_dir("").startswith("/api") %} 9 | {% set summary_split = obj.summary.split("/") %} 10 | {% set name = summary_split[-1] %} 11 | {% if name %} 12 | {{ name }} 13 | {{ "=" * name|length }} 14 | {% else %} 15 | {{ obj.name }} 16 | {{ "=" * obj.name|length }} 17 | {% endif %} 18 | 19 | .. py:module:: {{ obj.name }} 20 | 21 | {# Write docstring of module #} 22 | {% if obj.docstring %} 23 | .. autoapi-nested-parse:: 24 | 25 | {{ obj.docstring|prepare_docstring|indent(3) }} 26 | {% endif %} 27 | 28 | {% block api %} 29 | {# API. Write module name #} 30 | API - :mod:`{{ obj.name }}` 31 | {{ "-" * 13 }}{{ "-" * obj.name|length }} 32 | 33 | {% if obj.all is not none %} 34 | {# Get all visible children #} 35 | {% set visible_children = obj.children|selectattr("short_name", "in", obj.all)|list %} 36 | {% elif obj.type is equalto("package") %} 37 | {% set visible_children = obj.children|selectattr("display")|list %} 38 | {% else %} 39 | {% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %} 40 | {% endif %} 41 | {% if visible_children %} 42 | {# Write all visible children #} 43 | {% for obj_item in visible_children %} 44 | {{ obj_item.rendered|indent(0) }} 45 | {% endfor %} 46 | {% endif %} 47 | {% endblock %} 48 | 49 | {# Write submodules and subpackages if it is package #} 50 | {% block submodules %} 51 | {% set visible_submodules = obj.submodules|selectattr("display")|list %} 52 | {% set visible_subpackages = obj.subpackages|selectattr("display")|list %} 53 | {% if visible_submodules or visible_subpackages %} 54 | Submodules 55 | ---------- 56 | {% if visible_submodules %} 57 | 58 | .. toctree:: 59 | :titlesonly: 60 | :maxdepth: 1 61 | 62 | {% for submodule in visible_submodules %} 63 | {% if unincluded == submodule.include_dir("").startswith("/api") %} 64 | {{ submodule.name }} <{{ submodule.include_dir("") }}/index> 65 | {% endif %} 66 | {% endfor %} 67 | {% endif %} 68 | {% if visible_subpackages %} 69 | 70 | .. toctree:: 71 | :titlesonly: 72 | :maxdepth: 3 73 | 74 | {% for subpackage in visible_subpackages %} 75 | {% if unincluded == subpackage.include_dir("").startswith("/api") %} 76 | {{ subpackage.name }} <{{ subpackage.include_dir("") }}/index> 77 | {% endif %} 78 | {% endfor %} 79 | {% endif %} 80 | {% endif %} 81 | {% endblock %} 82 | -------------------------------------------------------------------------------- /docs/sources/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | License 5 | ------- 6 | 7 | Refer to `LICENSE `_. 8 | 9 | .. literalinclude:: ../../LICENSE 10 | :language: none 11 | :emphasize-lines: 1,4 12 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.100.0.rst: -------------------------------------------------------------------------------- 1 | 0.100.0 2 | -------- 3 | 4 | See on GitHub: `tag 0.100.0 `_ | `compare 0.99.99/0.100.0 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.100.0.zip 9 | 10 | * Added feature to change color for `MDStackFloatingButtons`. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.100.1.rst: -------------------------------------------------------------------------------- 1 | 0.100.1 2 | -------- 3 | 4 | See on GitHub: `tag 0.100.1 `_ | `compare 0.100.0/0.100.1 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.100.1.zip 9 | 10 | * `MDUserAnimationCard` uses `Image` instead of `AsyncImage`. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.100.2.rst: -------------------------------------------------------------------------------- 1 | 0.100.2 2 | -------- 3 | 4 | See on GitHub: `tag 0.100.2 `_ | `compare 0.100.1/0.100.2 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.100.2.zip 9 | 10 | * [Black](https://github.com/psf/black) formatting. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.0.rst: -------------------------------------------------------------------------------- 1 | 0.101.0 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.0 `_ | `compare 0.100.2/0.101.0 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.0.zip 9 | 10 | * Added `MDContextMenu` class. 11 | * Added `MDExpansionPanel` class. 12 | * Removed `MDAccordion` and `MDAccordionListItem`. Use `MDExpansionPanel` instead. 13 | * Added `HoverBehavior` class by [Olivier POYEN](https://gist.github.com/opqopq/15c707dc4cffc2b6455f). 14 | * Added markup support for buttons. 15 | * Added `duration` property to `Toast`. 16 | * Added `TextInput`'s events and properties to `MDTextFieldRound`. 17 | * Added feature to resize text field 18 | * Added color property to `MDSeparator` class 19 | * Added [tool](https://github.com/kivymd/KivyMD/blob/master/kivymd/tools/update_icons.py) for updating [Iconic font](https://github.com/Templarian/MaterialDesign-Webfont). 20 | * Updated [Iconic font](https://github.com/Templarian/MaterialDesign-Webfont) (v4.3.95). 21 | * Added new examples for [Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink). 22 | * Bug fixes and other minor improvements. 23 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.1.rst: -------------------------------------------------------------------------------- 1 | 0.101.1 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.1 `_ | `compare 0.101.0/0.101.1 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.1.zip 9 | 10 | * Bug fixes and other minor improvements. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.2.rst: -------------------------------------------------------------------------------- 1 | 0.101.2 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.2 `_ | `compare 0.101.1/0.101.2 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.2.zip 9 | 10 | * Bug fixes and other minor improvements. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.3.rst: -------------------------------------------------------------------------------- 1 | 0.101.3 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.3 `_ | `compare 0.101.2/0.101.3 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.3.zip 9 | 10 | * Bug fixes and other minor improvements. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.4.rst: -------------------------------------------------------------------------------- 1 | 0.101.4 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.4 `_ | `compare 0.101.3/0.101.4 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.4.zip 9 | 10 | * Bug fixes and other minor improvements. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.5.rst: -------------------------------------------------------------------------------- 1 | 0.101.5 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.5 `_ | `compare 0.101.4/0.101.5 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.5.zip 9 | 10 | * Added feature to see source code of current example ([Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink)). 11 | * Added names of authors of this fork ([Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink)). 12 | * Bug fixes and other minor improvements. 13 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.6.rst: -------------------------------------------------------------------------------- 1 | 0.101.6 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.6 `_ | `compare 0.101.5/0.101.6 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.6.zip 9 | 10 | * Fixed `NameError: name 'MDThemePicker' is not defined`. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.7.rst: -------------------------------------------------------------------------------- 1 | 0.101.7 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.7 `_ | `compare 0.101.6/0.101.7 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.7.zip 9 | 10 | * Fixed colors and position of the buttons in the `Buttons` demo screen ([Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink)). 11 | * Displaying percent of loading kv-files ([Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink)). 12 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.101.8.rst: -------------------------------------------------------------------------------- 1 | 0.101.8 2 | -------- 3 | 4 | See on GitHub: `tag 0.101.8 `_ | `compare 0.101.7/0.101.8 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.101.8.zip 9 | 10 | * Added `uix` and `behaviors` folder to `package_data`. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.102.0.rst: -------------------------------------------------------------------------------- 1 | 0.102.0 2 | -------- 3 | 4 | See on GitHub: `tag 0.102.0 `_ | `compare 0.101.8/0.102.0 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==0.102.0 9 | 10 | * Moved `kivymd.behaviors` to `kivymd.uix.behaviors`. 11 | * Updated [Iconic font](https://github.com/Templarian/MaterialDesign-Webfont) (v4.5.95). 12 | * Added `blank` icon to `icon_definitions`. 13 | * Bug fixes and other minor improvements. 14 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.102.1.rst: -------------------------------------------------------------------------------- 1 | 0.102.1 2 | -------- 3 | 4 | See on GitHub: `tag 0.102.1 `_ | `compare 0.102.0/0.102.1 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==0.102.1 9 | 10 | * Implemented the ability [Backdrop](https://material.io/components/backdrop) 11 | * Added `MDApp` class. Now app object should be inherited from `kivymd.app.MDApp`. 12 | * Added `MDRoundImageButton` class. 13 | * Added `MDTooltip` class. 14 | * Added `MDBanner` class. 15 | * Added hook for `PyInstaller` (add `hookspath=[kivymd.hooks_path]`). 16 | * Added examples of `spec` files for building [Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink). 17 | * Added some features to `MDProgressLoader`. 18 | * Added feature to preview the current value of `MDSlider`. 19 | * Added feature to use custom screens for dialog in `MDBottomSheet` class. 20 | * Removed `MDPopupScreen`. 21 | * Added [`studies`](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink/studies) directory for demos in Material Design. 22 | * Bug fixes and other minor improvements. 23 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.103.0.rst: -------------------------------------------------------------------------------- 1 | 0.103.0 2 | -------- 3 | 4 | See on GitHub: `tag 0.103.0 `_ | `compare 0.102.1/0.103.0 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==0.103.0 9 | 10 | * Fix `MDSwitch` size according to `material design` guides 11 | * Fix MDSwitch's thumb position when size changes 12 | * Fix position of the icon relative to the right edge of the `MDChip` class on mobile devices 13 | * Updated `MDBottomAppBar` class. 14 | * Updated `navigationdrawer.py` 15 | * Added `on_tab_switch` method that is called when switching tabs (`MDTabs` class) 16 | * Added `FpsMonitor` class 17 | * Added `fitimage.py` - feature to automatically crop a `Kivy` image to fit your layout 18 | * Added animation when changing the action button position mode in `MDBottomAppBar` class 19 | * Delete `fanscreenmanager.py` 20 | * Bug fixes and other minor improvements. 21 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.104.0.rst: -------------------------------------------------------------------------------- 1 | 0.104.0 2 | -------- 3 | 4 | See on GitHub: `tag 0.104.0 `_ | `compare 0.103.0/0.104.0 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==0.104.0 9 | 10 | * Fixed bug in :class:`kivymd.uix.expansionpanel.MDExpansionPanel` if, with the panel open, without closing it, try to open another panel, then the chevron of the first panel remained open. 11 | * The :class:`kivymd.uix.textfield.MDTextFieldRound` class is now directly inherited from the :class:`kivy.uix.textinput.TextInput` class. 12 | * Removed :class:`kivymd.uix.textfield.MDTextFieldClear` class. 13 | * :class:`kivymd.uix.navigationdrawer.NavigationLayout` allowed to add :class:`kivymd.uix.toolbar.MDToolbar` class. 14 | * Added feature to control range of dates to be active in :class:`kivymd.uix.picker.MDDatePicker` class. 15 | * Updated :class:`kivymd.uix.navigationdrawer.MDNavigationDrawer` realization. 16 | * Removed :class:`kivymd.uix.card.MDCardPost` class. 17 | * Added :class:`kivymd.uix.card.MDCardSwipe` class. 18 | * Added `switch_tab` method for switching tabs to :class:`kivymd.uix.bottomnavigation.MDBottomNavigation` class. 19 | * Added feature to use panel type in the :class:`kivymd.uix.expansionpanel.MDExpansionPanel` class: :class:`kivymd.uix.expansionpanel.MDExpansionPanelOneLine`, :class:`kivymd.uix.expansionpanel.MDExpansionPanelTwoLine` or :class:`kivymd.uix.expansionpanel.MDExpansionPanelThreeLine`. 20 | * Fixed panel opening animation in the :class:`kivymd.uix.expansionpanel.MDExpansionPanel` class. 21 | * Delete `kivymd.uix.managerswiper.py` 22 | * Add `MDFloatingActionButtonSpeedDial` class 23 | * Added the feature to create text on tabs using markup, thereby triggering the `on_ref_press` event in the `MDTabsLabel` class 24 | * Added `color_indicator` attribute to set custom indicator color in the `MDTabs` class 25 | * Added the feature to change the background color of menu items in the `BaseListItem` class 26 | * Add `MDTapTargetView` class -------------------------------------------------------------------------------- /docs/sources/changelog/0.104.1.rst: -------------------------------------------------------------------------------- 1 | 0.104.1 2 | -------- 3 | 4 | See on GitHub: `tag 0.104.1 `_ | `compare 0.104.0/0.104.1 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==0.104.1 9 | 10 | * Bug fixes and other minor improvements. 11 | * Added `MDGridLayout` and `MDBoxLayout` classes 12 | * Add `TouchBehavior` class 13 | * Add `radius` parameter to `BackgroundColorBehavior` class 14 | * Add `MDScreen` class 15 | * Add `MDFloatLayout` class 16 | * Added a `MDTextField` with `fill` mode 17 | * Added a shadow, increased speed of opening, added the feature to control the position of the `MDDropdownMenu` class 18 | * The `MDDropDownItem` class is now a regular element, such as a button 19 | * Added the ability to use the texture of the icon on the right in any `MDTextField` classes 20 | * Added the feature to use ripple and focus behavior in `MDCard` class 21 | * `MDDialogs` class redesigned to meet material design requirements 22 | * Added `MDDataTable` class -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.92.rst: -------------------------------------------------------------------------------- 1 | 0.99.92 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.92 `_ | `compare 0.99.91/0.99.92 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.92.zip 9 | 10 | * Removed automatic change of text field length in `MDTextFieldRound` class. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.93.rst: -------------------------------------------------------------------------------- 1 | 0.99.93 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.93 `_ | `compare 0.99.92/0.99.93 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.93.zip 9 | 10 | * Updated [Iconic font](https://github.com/Templarian/MaterialDesign-Webfont) (v3.6.95). 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.94.rst: -------------------------------------------------------------------------------- 1 | 0.99.94 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.94 `_ | `compare 0.99.93/0.99.94 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.94.zip 9 | 10 | * Added `_no_ripple_effect` property to `BaseListItem` class. 11 | * Added check to use `ripple effect` in `RectangularRippleBehavior` class. 12 | * [Disabled](https://www.youtube.com/watch?v=P_9oSx0Pz_U) using `ripple effect` in `MDAccordionListItem` class. 13 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.95.rst: -------------------------------------------------------------------------------- 1 | 0.99.95 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.95 `_ | `compare 0.99.94/0.99.95 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.95.zip 9 | 10 | * Added function to create a round image in `kivymd/utils/cropimage.py` module. 11 | * Added `MDCustomRoundIconButton` class. 12 | * Added demo application [Account Page](https://www.youtube.com/watch?v=dfUOwqtYoYg) for [Kitchen Sink demo](https://github.com/kivymd/KivyMD/tree/master/demos/kitchen_sink). 13 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.96.rst: -------------------------------------------------------------------------------- 1 | 0.99.96 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.96 `_ | `compare 0.99.95/0.99.96 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.96.zip 9 | 10 | * Added `asynckivy` module by [Nattōsai Mitō](https://github.com/gottadiveintopython/asynckivy). 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.97.rst: -------------------------------------------------------------------------------- 1 | 0.99.97 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.97 `_ | `compare 0.99.96/0.99.97 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.97.zip 9 | 10 | * Fixed `Spinner` animation. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.98.rst: -------------------------------------------------------------------------------- 1 | 0.99.98 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.98 `_ | `compare 0.99.97/0.99.98 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.98.zip 9 | 10 | * Added `MDFillRoundFlatIconButton` class. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.99.01.rst: -------------------------------------------------------------------------------- 1 | 0.99.99.01 2 | ----------- 3 | 4 | See on GitHub: `tag 0.99.99.01 `_ | `compare 0.99.98/0.99.99.01 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.99.01.zip 9 | 10 | * Fixed `MDNavigationDrawer.use_logo`. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/0.99.99.rst: -------------------------------------------------------------------------------- 1 | 0.99.99 2 | -------- 3 | 4 | See on GitHub: `tag 0.99.99 `_ | `compare 0.99.99.01/0.99.99 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/0.99.99.zip 9 | 10 | * Added `icon_color` property for `NavigationDrawerIconButton`. 11 | -------------------------------------------------------------------------------- /docs/sources/changelog/1.0.1.rst: -------------------------------------------------------------------------------- 1 | 1.0.1 2 | ------ 3 | 4 | See on GitHub: `tag 1.0.1 `_ | `compare 1.0.0/1.0.1 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==1.0.1 9 | 10 | * Bug fixes and other minor improvements. 11 | * Fix https://github.com/kivymd/KivyMD/issues/1305. 12 | -------------------------------------------------------------------------------- /docs/sources/changelog/1.0.2.rst: -------------------------------------------------------------------------------- 1 | 1.0.2 2 | ------ 3 | 4 | See on GitHub: `tag 1.0.2 `_ | `compare 1.0.1/1.0.2 `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install kivymd==1.0.2 9 | 10 | * Bug fixes and other minor improvements. 11 | * Added a button to copy the code to the documentation. 12 | * Added the feature to view code examples of documentation in imperative and declarative styles. 13 | * Added console scripts for developer tools. 14 | -------------------------------------------------------------------------------- /docs/sources/changelog/index.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | .. include:: /changelog/unreleased.rst 5 | .. include:: /changelog/2.0.0.rst 6 | .. include:: /changelog/1.1.1.rst 7 | .. include:: /changelog/1.0.2.rst 8 | .. include:: /changelog/1.0.1.rst 9 | .. include:: /changelog/1.0.0.rst 10 | .. include:: /changelog/0.104.2.rst 11 | .. include:: /changelog/0.104.1.rst 12 | .. include:: /changelog/0.104.0.rst 13 | .. include:: /changelog/0.103.0.rst 14 | .. include:: /changelog/0.102.1.rst 15 | .. include:: /changelog/0.102.0.rst 16 | .. include:: /changelog/0.101.8.rst 17 | .. include:: /changelog/0.101.7.rst 18 | .. include:: /changelog/0.101.6.rst 19 | .. include:: /changelog/0.101.5.rst 20 | .. include:: /changelog/0.101.4.rst 21 | .. include:: /changelog/0.101.3.rst 22 | .. include:: /changelog/0.101.2.rst 23 | .. include:: /changelog/0.101.1.rst 24 | .. include:: /changelog/0.101.0.rst 25 | .. include:: /changelog/0.100.2.rst 26 | .. include:: /changelog/0.100.1.rst 27 | .. include:: /changelog/0.100.0.rst 28 | .. include:: /changelog/0.99.99.01.rst 29 | .. include:: /changelog/0.99.99.rst 30 | .. include:: /changelog/0.99.98.rst 31 | .. include:: /changelog/0.99.97.rst 32 | .. include:: /changelog/0.99.96.rst 33 | .. include:: /changelog/0.99.95.rst 34 | .. include:: /changelog/0.99.94.rst 35 | .. include:: /changelog/0.99.93.rst 36 | .. include:: /changelog/0.99.92.rst 37 | -------------------------------------------------------------------------------- /docs/sources/changelog/unreleased.rst: -------------------------------------------------------------------------------- 1 | Unreleased 2 | ---------- 3 | 4 | See on GitHub: `branch master `_ | `compare 2.0.0/master `_ 5 | 6 | .. code-block:: bash 7 | 8 | pip install https://github.com/kivymd/KivyMD/archive/master.zip 9 | 10 | * Bug fixes and other minor improvements. 11 | * [`compare `_] Add supports `Google's Material Design 3` and the `Material You` concept. 12 | * [`commit 25f242e `_] Add the feature to use icons from custom fonts. 13 | * [`pull 1584 `_] Implement bitmap scale down. 14 | * [`commit 8ba6af9 `_] Fix [`issue 1593 `_]. 15 | * [`commit b34b07f `_] Fix elevation properties. 16 | * [`commit cb01c01 `_] Fixed an infinite loop when typing text fast in the `MDTextfield` widget. 17 | -------------------------------------------------------------------------------- /docs/sources/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to KivyMD's documentation! 2 | ================================== 3 | 4 | .. autoapimodule:: kivymd 5 | :noindex: 6 | 7 | Contents 8 | -------- 9 | 10 | .. toctree:: 11 | :hidden: 12 | 13 | Welcome 14 | 15 | .. toctree:: 16 | :maxdepth: 1 17 | 18 | /getting-started 19 | /themes/index 20 | /components/index 21 | /controllers/index 22 | /behaviors/index 23 | /effects/index 24 | /templates/index 25 | /changelog/index 26 | /about 27 | 28 | .. toctree:: 29 | :maxdepth: 1 30 | :titlesonly: 31 | 32 | API 33 | 34 | Indices and tables 35 | ------------------ 36 | 37 | * :ref:`genindex` 38 | * :ref:`modindex` 39 | * :ref:`search` 40 | -------------------------------------------------------------------------------- /docs/sources/themes/index.rst: -------------------------------------------------------------------------------- 1 | Themes 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | /themes/theming/index 8 | /themes/material-app/index 9 | /themes/icon-definitions/index 10 | /themes/font-definitions/index 11 | -------------------------------------------------------------------------------- /examples/appbar.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.secondaryContainerColor 9 | 10 | MDIconButton: 11 | on_release: app.open_menu(self) 12 | pos_hint: {"top": .98} 13 | x: "12dp" 14 | icon: "menu" 15 | 16 | MDBoxLayout: 17 | orientation: "vertical" 18 | adaptive_height: True 19 | size_hint_x: .8 20 | spacing: "12dp" 21 | pos_hint: {"center_x": .5, "center_y": .5} 22 | 23 | MDTopAppBar: 24 | id: appbar 25 | type: "small" 26 | 27 | MDTopAppBarLeadingButtonContainer: 28 | 29 | MDActionTopAppBarButton: 30 | icon: "arrow-left" 31 | 32 | MDTopAppBarTitle: 33 | text: "AppBar small" 34 | 35 | MDTopAppBarTrailingButtonContainer: 36 | 37 | MDActionTopAppBarButton: 38 | icon: "attachment" 39 | 40 | MDActionTopAppBarButton: 41 | icon: "calendar" 42 | 43 | MDActionTopAppBarButton: 44 | icon: "dots-vertical" 45 | 46 | MDTopAppBar: 47 | id: appbar_custom 48 | type: "small" 49 | 50 | MDTopAppBarLeadingButtonContainer: 51 | 52 | MDActionTopAppBarButton: 53 | icon: "arrow-left" 54 | theme_icon_color: "Custom" 55 | icon_color: "green" 56 | 57 | MDTopAppBarTitle: 58 | text: "AppBar small" 59 | theme_text_color: "Custom" 60 | text_color: "green" 61 | 62 | MDTopAppBarTrailingButtonContainer: 63 | 64 | MDActionTopAppBarButton: 65 | icon: "attachment" 66 | theme_icon_color: "Custom" 67 | icon_color: "green" 68 | 69 | MDActionTopAppBarButton: 70 | icon: "calendar" 71 | theme_icon_color: "Custom" 72 | icon_color: "green" 73 | 74 | MDActionTopAppBarButton: 75 | icon: "dots-vertical" 76 | theme_icon_color: "Custom" 77 | icon_color: "green" 78 | """ 79 | 80 | 81 | class Example(MDApp, CommonApp): 82 | def build(self): 83 | return Builder.load_string(KV) 84 | 85 | def disabled_widgets(self): 86 | self.root.ids.appbar.disabled = not self.root.ids.appbar.disabled 87 | self.root.ids.appbar_custom.disabled = self.root.ids.appbar.disabled 88 | 89 | 90 | Example().run() 91 | -------------------------------------------------------------------------------- /examples/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/examples/avatar.png -------------------------------------------------------------------------------- /examples/badge.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.backgroundColor 9 | 10 | MDIconButton: 11 | on_release: app.open_menu(self) 12 | pos_hint: {"top": .98} 13 | x: "12dp" 14 | icon: "menu" 15 | 16 | MDIcon: 17 | id: icon 18 | icon: "gmail" 19 | pos_hint: {'center_x': .5, 'center_y': .5} 20 | 21 | MDBadge: 22 | text: "12" 23 | """ 24 | 25 | 26 | class Example(MDApp, CommonApp): 27 | def build(self): 28 | return Builder.load_string(KV) 29 | 30 | def disabled_widgets(self): 31 | self.root.ids.icon.disabled = not self.root.ids.icon.disabled 32 | 33 | 34 | Example().run() 35 | -------------------------------------------------------------------------------- /examples/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/examples/bg.jpg -------------------------------------------------------------------------------- /examples/bottomsheet.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.backgroundColor 9 | 10 | MDIconButton: 11 | on_release: app.open_menu(self) 12 | pos_hint: {"top": .98} 13 | x: dp(12) 14 | icon: "menu" 15 | 16 | MDNavigationLayout: 17 | 18 | MDScreenManager: 19 | 20 | MDScreen: 21 | 22 | MDBoxLayout: 23 | spacing: "24dp" 24 | pos_hint: {"center_x": .5, "center_y": .7} 25 | adaptive_size: True 26 | 27 | MDButton: 28 | on_release: app.open_sheet(sheet, "standard") 29 | 30 | MDButtonText: 31 | text: "Open standard sheet" 32 | 33 | MDButton: 34 | on_release: app.open_sheet(sheet, "modal") 35 | 36 | MDButtonText: 37 | text: "Open modal sheet" 38 | 39 | MDBottomSheet: 40 | id: sheet 41 | size_hint_y: None 42 | height: "320dp" 43 | background_color: self.theme_cls.surfaceColor 44 | drawer_type: "standard" 45 | 46 | MDBottomSheetDragHandle: 47 | 48 | MDBottomSheetDragHandleTitle: 49 | text: "MDBottomSheet" 50 | adaptive_height: True 51 | pos_hint: {"center_y": .5} 52 | 53 | MDBottomSheetDragHandleButton: 54 | icon: "close" 55 | on_release: sheet.set_state("toggle") 56 | 57 | BoxLayout: 58 | orientation: "vertical" 59 | 60 | Widget: 61 | """ 62 | 63 | 64 | class Example(MDApp, CommonApp): 65 | def build(self): 66 | self.theme_cls.primary_palette = "Olive" 67 | return Builder.load_string(KV) 68 | 69 | def open_sheet(self, sheet, drawer_type): 70 | sheet.drawer_type = drawer_type 71 | sheet.set_state("toggle") 72 | 73 | def disabled_widgets(self): ... 74 | 75 | 76 | Example().run() 77 | -------------------------------------------------------------------------------- /examples/checkbox.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import KV, CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.uix.selectioncontrol import MDCheckbox 6 | 7 | 8 | class Example(MDApp, CommonApp): 9 | def build(self): 10 | return Builder.load_string(KV) 11 | 12 | def on_start(self): 13 | self.root.ids.widget_box.add_widget(MDCheckbox()) 14 | self.root.ids.custom_widget_box.add_widget( 15 | MDCheckbox( 16 | color_active="lightgreen", 17 | color_inactive="red", 18 | color_disabled="brown", 19 | ) 20 | ) 21 | 22 | 23 | Example().run() 24 | -------------------------------------------------------------------------------- /examples/chip.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import KV, CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.uix.chip import MDChip, MDChipLeadingIcon, MDChipText 6 | 7 | 8 | class Example(MDApp, CommonApp): 9 | def build(self): 10 | self.theme_cls.primary_palette = "Olive" 11 | return Builder.load_string(KV) 12 | 13 | def on_start(self): 14 | for chip_type in ["assist", "input", "suggestion", "filter"]: 15 | self.root.ids.widget_box.add_widget( 16 | MDChip( 17 | MDChipLeadingIcon( 18 | icon="account", 19 | ), 20 | MDChipText( 21 | text=chip_type.capitalize(), 22 | ), 23 | type=chip_type, 24 | ) 25 | ) 26 | 27 | for chip_type in ["assist", "input", "suggestion", "filter"]: 28 | self.root.ids.custom_widget_box.add_widget( 29 | MDChip( 30 | MDChipLeadingIcon( 31 | icon="account", 32 | theme_icon_color="Custom", 33 | icon_color="brown", 34 | icon_color_disabled="black", 35 | ), 36 | MDChipText( 37 | text=chip_type.capitalize(), 38 | theme_text_color="Custom", 39 | text_color="red", 40 | text_color_disabled="black", 41 | ), 42 | type=chip_type, 43 | theme_line_color="Custom", 44 | line_color="green", 45 | line_color_disabled="black", 46 | ) 47 | ) 48 | 49 | 50 | Example().run() 51 | -------------------------------------------------------------------------------- /examples/dropdownitem.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.uix.menu import MDDropdownMenu 6 | 7 | KV = """ 8 | MDScreen 9 | md_bg_color: self.theme_cls.backgroundColor 10 | 11 | MDIconButton: 12 | on_release: app.open_menu(self) 13 | pos_hint: {"top": .98} 14 | x: "12dp" 15 | icon: "menu" 16 | 17 | MDDropDownItem: 18 | id: drop_item 19 | pos_hint: {"center_x": .5, "center_y": .5} 20 | on_release: app.open_drop_item_menu(self) 21 | 22 | MDDropDownItemText: 23 | id: drop_text 24 | text: "Item" 25 | """ 26 | 27 | 28 | class Example(MDApp, CommonApp): 29 | drop_item_menu: MDDropdownMenu = None 30 | 31 | def open_drop_item_menu(self, item): 32 | menu_items = [ 33 | { 34 | "text": f"{i}", 35 | "on_release": lambda x=f"Item {i}": self.menu_callback(x), 36 | } 37 | for i in range(5) 38 | ] 39 | if not self.drop_item_menu: 40 | self.drop_item_menu = MDDropdownMenu( 41 | caller=item, items=menu_items, position="center" 42 | ) 43 | self.drop_item_menu.open() 44 | 45 | def menu_callback(self, text_item): 46 | self.root.ids.drop_text.text = text_item 47 | self.drop_item_menu.dismiss() 48 | 49 | def disabled_widgets(self): 50 | self.root.ids.drop_item.disabled = not self.root.ids.drop_item.disabled 51 | 52 | def build(self): 53 | return Builder.load_string(KV) 54 | 55 | 56 | Example().run() 57 | -------------------------------------------------------------------------------- /examples/dynamic_color_image.py: -------------------------------------------------------------------------------- 1 | # Drag the image to the test application window. 2 | 3 | import os 4 | 5 | from kivy.clock import Clock 6 | from kivy.core.window import Window 7 | from kivy.core.window.window_sdl2 import WindowSDL 8 | from kivy.lang import Builder 9 | from kivy.properties import ColorProperty, StringProperty 10 | 11 | from kivymd.app import MDApp 12 | from kivymd.uix.boxlayout import MDBoxLayout 13 | 14 | KV = """ 15 | 16 | orientation: "vertical" 17 | 18 | MDLabel: 19 | text: root.text 20 | color: "grey" 21 | adaptive_height: True 22 | 23 | MDCard: 24 | theme_bg_color: "Custom" 25 | md_bg_color: root.bg_color 26 | 27 | 28 | MDScreen: 29 | md_bg_color: app.theme_cls.backgroundColor 30 | 31 | MDRecycleView: 32 | id: card_list 33 | viewclass: "ColorCard" 34 | bar_width: 0 35 | 36 | RecycleGridLayout: 37 | cols: 3 38 | spacing: "16dp" 39 | padding: "16dp" 40 | default_size: None, dp(56) 41 | default_size_hint: 1, None 42 | size_hint_y: None 43 | height: self.minimum_height 44 | """ 45 | 46 | 47 | class ColorCard(MDBoxLayout): 48 | text = StringProperty() 49 | bg_color = ColorProperty() 50 | 51 | 52 | class Example(MDApp): 53 | def __init__(self, **kwargs): 54 | super().__init__(**kwargs) 55 | Window.bind(on_dropfile=self.on_drop_file) 56 | 57 | def on_drop_file(self, sdl: WindowSDL, path_to_file: str) -> None: 58 | ext = os.path.splitext(path_to_file)[1] 59 | if isinstance(path_to_file, bytes): 60 | path_to_file = path_to_file.decode() 61 | if isinstance(ext, bytes): 62 | ext = ext.decode() 63 | if ext in [".png", ".jpg"]: 64 | self.theme_cls.path_to_wallpaper = path_to_file 65 | Clock.schedule_once(self.generate_cards, 0.5) 66 | 67 | def build(self): 68 | self.theme_cls.dynamic_color = True 69 | self.theme_cls.theme_style = "Dark" 70 | return Builder.load_string(KV) 71 | 72 | def theme_switch(self) -> None: 73 | self.theme_cls.switch_theme() 74 | Clock.schedule_once(self.generate_cards, 0.5) 75 | 76 | def generate_cards(self, *args): 77 | self.root.ids.card_list.data = [] 78 | for name_color in self.theme_cls.current_schemes_color_data: 79 | self.root.ids.card_list.data.append( 80 | { 81 | "bg_color": getattr(self.theme_cls, name_color), 82 | "text": name_color, 83 | } 84 | ) 85 | 86 | def on_start(self): 87 | Clock.schedule_once(self.generate_cards) 88 | 89 | 90 | Example().run() 91 | -------------------------------------------------------------------------------- /examples/dynamic_color_schemes.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | from kivy.uix.boxlayout import BoxLayout 4 | 5 | from examples.common_app import KV, CommonApp 6 | from kivymd.app import MDApp 7 | from kivymd.dynamic_color import DynamicColor 8 | from kivymd.uix.boxlayout import MDBoxLayout 9 | 10 | Builder.load_string( 11 | """ 12 | #:import Clipboard kivy.core.clipboard.Clipboard 13 | 14 | : 15 | name: "primaryColor" 16 | color:[0,0,0,0] 17 | size_hint_y:None 18 | height:dp(130) 19 | orientation:"vertical" 20 | spacing:dp(10) 21 | BoxLayout: 22 | spacing:dp(10) 23 | MDIconButton: 24 | icon:"content-copy" 25 | size_hint_x:None 26 | width:dp(50) 27 | on_release: 28 | Clipboard.copy(root.name) 29 | MDLabel: 30 | text:root.name 31 | adaptive_height:True 32 | MDBoxLayout: 33 | md_bg_color:root.color 34 | radius:dp(10) 35 | 36 | : 37 | ScrollView: 38 | MDBoxLayout: 39 | orientation:"vertical" 40 | id:main_view 41 | adaptive_height:True 42 | spacing:dp(20) 43 | """ 44 | ) 45 | 46 | 47 | class Container(MDBoxLayout): 48 | pass 49 | 50 | 51 | class DynamicColorInfo(BoxLayout): 52 | pass 53 | 54 | 55 | class Example(MDApp, CommonApp): 56 | def build(self): 57 | self.theme_cls.dynamic_color = True 58 | self.theme_cls.path_to_wallpaper = "path_to_some_image.png" 59 | self.theme_cls.on_colors = lambda: Clock.schedule_once(self.refresh) 60 | return Builder.load_string(KV) 61 | 62 | def on_start(self): 63 | parent_widget = self.root.ids.widget_box.parent.parent 64 | parent_widget.clear_widgets() 65 | self.container = Container() 66 | parent_widget.add_widget(self.container) 67 | self.container.ids.main_view.clear_widgets() 68 | 69 | for color in vars(DynamicColor).keys(): 70 | if "__" in color: 71 | continue 72 | widget = DynamicColorInfo() 73 | widget.name = color 74 | widget.color = getattr(self.theme_cls, color) 75 | self.container.ids.main_view.add_widget(widget) 76 | 77 | Clock.schedule_once(self.refresh) 78 | 79 | def refresh(self, *arg): 80 | for widget in self.container.ids.main_view.children: 81 | widget.color = getattr(self.theme_cls, widget.name) 82 | 83 | 84 | Example().run() 85 | -------------------------------------------------------------------------------- /examples/hero.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from kivymd.app import MDApp 4 | 5 | KV = """ 6 | MDScreenManager: 7 | 8 | MDScreen: 9 | name: "screen A" 10 | md_bg_color: "lightblue" 11 | 12 | MDHeroFrom: 13 | id: hero_from 14 | tag: "hero" 15 | size_hint: None, None 16 | size: "120dp", "120dp" 17 | pos_hint: {"top": .98} 18 | x: 24 19 | 20 | FitImage: 21 | source: "bg.jpg" 22 | size_hint: None, None 23 | size: hero_from.size 24 | 25 | MDButton: 26 | pos_hint: {"center_x": .5} 27 | y: "36dp" 28 | on_release: 29 | root.current_heroes = ["hero"] 30 | root.current = "screen B" 31 | 32 | MDButtonText: 33 | text: "Move Hero To Screen B" 34 | 35 | MDScreen: 36 | name: "screen B" 37 | hero_to: hero_to 38 | md_bg_color: "cadetblue" 39 | 40 | MDHeroTo: 41 | id: hero_to 42 | tag: "hero" 43 | size_hint: None, None 44 | size: "220dp", "220dp" 45 | pos_hint: {"center_x": .5, "center_y": .5} 46 | 47 | MDButton: 48 | pos_hint: {"center_x": .5} 49 | y: "36dp" 50 | on_release: 51 | root.current_heroes = ["hero"] 52 | root.current = "screen A" 53 | 54 | MDButtonText: 55 | text: "Move Hero To Screen A" 56 | """ 57 | 58 | 59 | class Example(MDApp): 60 | def build(self): 61 | return Builder.load_string(KV) 62 | 63 | 64 | Example().run() 65 | -------------------------------------------------------------------------------- /examples/imagelist.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | from kivy.properties import VariableListProperty 3 | 4 | from examples.common_app import CommonApp 5 | from kivymd.app import MDApp 6 | from kivymd.uix.imagelist import MDSmartTile 7 | 8 | KV = """ 9 | 10 | size_hint: None, None 11 | size: "320dp", "320dp" 12 | 13 | MDSmartTileImage: 14 | source: "bg.jpg" 15 | radius: root.image_radius 16 | 17 | MDSmartTileOverlayContainer: 18 | md_bg_color: 0, 0, 0, .5 19 | adaptive_height: True 20 | padding: "8dp" 21 | spacing: "8dp" 22 | radius: root.container_radius 23 | 24 | MDIconButton: 25 | icon: "heart-outline" 26 | pos_hint: {"center_y": .5} 27 | on_release: 28 | self.icon = "heart" \\ 29 | if self.icon == "heart-outline" else \\ 30 | "heart-outline" 31 | 32 | MDLabel: 33 | text: "Ibanez GRG121DX-BKF" 34 | theme_text_color: "Custom" 35 | text_color: "white" 36 | 37 | 38 | MDScreen: 39 | md_bg_color: self.theme_cls.backgroundColor 40 | 41 | MDIconButton: 42 | on_release: app.open_menu(self) 43 | pos_hint: {"top": .98} 44 | x: "12dp" 45 | icon: "menu" 46 | 47 | MDBoxLayout: 48 | adaptive_size: True 49 | spacing: "24dp" 50 | pos_hint: {"center_x": .5, "center_y": .5} 51 | 52 | SmartTile: 53 | overlap: True 54 | image_radius: [dp(24), ] 55 | container_radius: [0, 0, dp(24), dp(24)] 56 | 57 | SmartTile: 58 | overlap: False 59 | image_radius: [dp(24), dp(24), 0, 0] 60 | container_radius: [0, 0, dp(24), dp(24)] 61 | """ 62 | 63 | 64 | class SmartTile(MDSmartTile): 65 | image_radius = VariableListProperty([0], length=4) 66 | container_radius = VariableListProperty([0], length=4) 67 | 68 | 69 | class Example(MDApp, CommonApp): 70 | def build(self): 71 | self.theme_cls.theme_style = "Dark" 72 | return Builder.load_string(KV) 73 | 74 | def disabled_widgets(self): ... 75 | 76 | 77 | Example().run() 78 | -------------------------------------------------------------------------------- /examples/label.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.font_definitions import theme_font_styles 6 | 7 | KV = """ 8 | MDScreen: 9 | md_bg_color: self.theme_cls.backgroundColor 10 | 11 | MDIconButton: 12 | on_release: app.open_menu(self) 13 | pos_hint: {"top": .98} 14 | x: "12dp" 15 | icon: "menu" 16 | 17 | MDRecycleView: 18 | id: rv 19 | key_viewclass: 'viewclass' 20 | key_size: 'height' 21 | size_hint_y: None 22 | height: root.height - dp(48) 23 | 24 | RecycleBoxLayout: 25 | padding: dp(18) 26 | spacing: dp(10) 27 | default_size: None, dp(48) 28 | default_size_hint: 1, None 29 | size_hint_y: None 30 | height: self.minimum_height 31 | orientation: "vertical" 32 | """ 33 | 34 | 35 | class Example(MDApp, CommonApp): 36 | _disabled = False 37 | 38 | def build(self): 39 | return Builder.load_string(KV) 40 | 41 | def disabled_widgets(self): 42 | self._disabled = not self._disabled 43 | self.create_widgets() 44 | 45 | def create_widgets(self): 46 | self.root.ids.rv.data = [] 47 | for style in theme_font_styles: 48 | if style != "Icon": 49 | for role in theme_font_styles[style]: 50 | font_size = int(theme_font_styles[style][role]["font-size"]) 51 | self.root.ids.rv.data.append( 52 | { 53 | "viewclass": "MDLabel", 54 | "text": f"{style} {role} {font_size} sp", 55 | "adaptive_height": "True", 56 | "font_style": style, 57 | "role": role, 58 | "disabled": self._disabled, 59 | } 60 | ) 61 | 62 | def on_start(self): 63 | self.create_widgets() 64 | 65 | 66 | Example().run() 67 | -------------------------------------------------------------------------------- /examples/list.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import KV, CommonApp 4 | from kivymd import images_path 5 | from kivymd.app import MDApp 6 | from kivymd.uix.list import ( 7 | MDListItem, 8 | MDListItemHeadlineText, 9 | MDListItemLeadingAvatar, 10 | MDListItemLeadingIcon, 11 | MDListItemSupportingText, 12 | MDListItemTertiaryText, 13 | MDListItemTrailingCheckbox, 14 | ) 15 | 16 | 17 | class Example(MDApp, CommonApp): 18 | def build(self): 19 | return Builder.load_string(KV) 20 | 21 | def on_tap_list_item(self, list_item: MDListItem): 22 | print("on_tap_list_item") 23 | 24 | def on_start(self): 25 | self.root.ids.widget_box.orientation = "vertical" 26 | self.root.ids.widget_box.add_widget( 27 | MDListItem( 28 | MDListItemLeadingIcon( 29 | icon="account-outline", 30 | ), 31 | MDListItemHeadlineText( 32 | text="Headline", 33 | ), 34 | MDListItemSupportingText( 35 | text="Supporting text", 36 | ), 37 | MDListItemTertiaryText( 38 | text="Tertiary text", 39 | ), 40 | MDListItemTrailingCheckbox(), 41 | on_release=self.on_tap_list_item, 42 | ) 43 | ) 44 | # Custom. 45 | self.root.ids.custom_widget_box.add_widget( 46 | MDListItem( 47 | MDListItemLeadingAvatar( 48 | source=f"{images_path}/logo/kivymd-icon-256.png", 49 | ), 50 | MDListItemHeadlineText( 51 | text="Headline", 52 | ), 53 | MDListItemTrailingCheckbox( 54 | color_disabled="red", 55 | ), 56 | divider=True, 57 | theme_divider_color="Custom", 58 | divider_color="red", 59 | theme_bg_color="Custom", 60 | md_bg_color=[1, 1, 0, 0.3], 61 | ) 62 | ) 63 | 64 | 65 | Example().run() 66 | -------------------------------------------------------------------------------- /examples/md_transitions.py: -------------------------------------------------------------------------------- 1 | from kivy.animation import Animation 2 | from kivy.clock import Clock 3 | from kivy.lang import Builder 4 | from kivy.metrics import dp 5 | from kivy.properties import ListProperty 6 | from kivy.uix.boxlayout import BoxLayout 7 | 8 | from kivymd.app import MDApp 9 | 10 | 11 | class AnimBox(BoxLayout): 12 | obj_pos = ListProperty([0, 0]) 13 | 14 | 15 | UI = """ 16 | : 17 | transition:"in_out_bounce" 18 | size_hint_y:None 19 | height:dp(100) 20 | obj_pos:[dp(40), self.pos[-1] + dp(40)] 21 | canvas: 22 | Color: 23 | rgba:app.theme_cls.primaryContainerColor 24 | Rectangle: 25 | size:[self.size[0], dp(5)] 26 | pos:self.pos[0], self.pos[-1] + dp(50) 27 | Color: 28 | rgba:app.theme_cls.primaryColor 29 | Rectangle: 30 | size:[dp(30)] * 2 31 | pos:root.obj_pos 32 | MDLabel: 33 | adaptive_height:True 34 | text:root.transition 35 | padding:[dp(10), 0] 36 | halign:"center" 37 | 38 | MDGridLayout: 39 | orientation:"lr-tb" 40 | cols:1 41 | md_bg_color:app.theme_cls.backgroundColor 42 | spacing:dp(10) 43 | """ 44 | 45 | 46 | class MotionApp(MDApp): 47 | def build(self): 48 | return Builder.load_string(UI) 49 | 50 | def on_start(self): 51 | for transition in [ 52 | "easing_linear", 53 | "easing_accelerated", 54 | "easing_decelerated", 55 | "easing_standard", 56 | "in_out_cubic", 57 | ]: # Add more here for comparison 58 | print(transition) 59 | widget = AnimBox() 60 | widget.transition = transition 61 | self.root.add_widget(widget) 62 | Clock.schedule_once(self.run_animation, 1) 63 | 64 | _inverse = True 65 | 66 | def run_animation(self, dt): 67 | x = (self.root.children[0].width - dp(30)) if self._inverse else 0 68 | for widget in self.root.children: 69 | Animation( 70 | obj_pos=[x, widget.obj_pos[-1]], t=widget.transition, d=3 71 | ).start(widget) 72 | self._inverse = not self._inverse 73 | Clock.schedule_once(self.run_animation, 3.1) 74 | 75 | 76 | MotionApp().run() 77 | -------------------------------------------------------------------------------- /examples/navigation_rail.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | from kivy.properties import StringProperty 3 | 4 | from examples.common_app import CommonApp 5 | from kivymd.app import MDApp 6 | from kivymd.uix.navigationrail import MDNavigationRailItem 7 | 8 | KV = """ 9 | 10 | 11 | MDNavigationRailItemIcon: 12 | icon: root.icon 13 | 14 | MDNavigationRailItemLabel: 15 | text: root.text 16 | 17 | 18 | MDBoxLayout: 19 | 20 | MDNavigationRail: 21 | id: rail 22 | 23 | MDNavigationRailMenuButton: 24 | icon: "menu" 25 | on_release: app.open_menu(self) 26 | 27 | MDNavigationRailFabButton: 28 | icon: "home" 29 | 30 | CommonNavigationRailItem: 31 | icon: "folder-outline" 32 | text: "Files" 33 | 34 | CommonNavigationRailItem: 35 | icon: "bookmark-outline" 36 | text: "Bookmark" 37 | 38 | CommonNavigationRailItem: 39 | icon: "library-outline" 40 | text: "Library" 41 | 42 | MDScreen: 43 | md_bg_color: self.theme_cls.secondaryContainerColor 44 | """ 45 | 46 | 47 | class CommonNavigationRailItem(MDNavigationRailItem): 48 | text = StringProperty() 49 | icon = StringProperty() 50 | 51 | 52 | class Example(MDApp, CommonApp): 53 | def build(self): 54 | return Builder.load_string(KV) 55 | 56 | def disabled_widgets(self): 57 | self.root.ids.rail.disabled = not self.root.ids.rail.disabled 58 | 59 | 60 | Example().run() 61 | -------------------------------------------------------------------------------- /examples/segmented_button.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.backgroundColor 9 | 10 | MDIconButton: 11 | on_release: app.open_menu(self) 12 | pos_hint: {"top": .98} 13 | x: "12dp" 14 | icon: "menu" 15 | 16 | MDBoxLayout: 17 | orientation: "vertical" 18 | padding: "32dp", "72dp", "32dp", "32dp" 19 | spacing: "24dp" 20 | 21 | MDLabel: 22 | adaptive_height: True 23 | text: "Segmented button" 24 | 25 | MDSegmentedButton: 26 | id: segmented_button 27 | 28 | MDSegmentedButtonItem: 29 | 30 | MDSegmentButtonIcon: 31 | icon: "language-python" 32 | 33 | MDSegmentButtonLabel: 34 | text: "Python" 35 | 36 | MDSegmentedButtonItem: 37 | 38 | MDSegmentButtonIcon: 39 | icon: "language-javascript" 40 | 41 | MDSegmentButtonLabel: 42 | text: "Java-Script" 43 | 44 | MDLabel: 45 | adaptive_height: True 46 | text: "Custom Segmented button" 47 | 48 | MDSegmentedButton: 49 | id: segmented_button_custom 50 | selected_icon_color: "red" 51 | 52 | MDSegmentedButtonItem: 53 | theme_line_color: "Custom" 54 | line_color: "red" 55 | selected_color: "#a655f240" 56 | 57 | MDSegmentButtonIcon: 58 | icon: "language-python" 59 | theme_icon_color: "Custom" 60 | icon_color: "red" 61 | 62 | MDSegmentButtonLabel: 63 | text: "Python" 64 | theme_text_color: "Custom" 65 | text_color: "red" 66 | 67 | MDSegmentedButtonItem: 68 | theme_line_color: "Custom" 69 | line_color: "red" 70 | selected_color: "#a655f240" 71 | 72 | MDSegmentButtonIcon: 73 | icon: "language-javascript" 74 | theme_icon_color: "Custom" 75 | icon_color: "red" 76 | 77 | MDSegmentButtonLabel: 78 | text: "Java-Script" 79 | theme_text_color: "Custom" 80 | text_color: "red" 81 | 82 | MDWidget: 83 | """ 84 | 85 | 86 | class Example(MDApp, CommonApp): 87 | def build(self): 88 | self.theme_cls.primary_palette = "Olive" 89 | return Builder.load_string(KV) 90 | 91 | def disabled_widgets(self): 92 | for item in ( 93 | self.root.ids.segmented_button.get_items() 94 | + self.root.ids.segmented_button_custom.get_items() 95 | ): 96 | item.disabled = not item.disabled 97 | 98 | 99 | Example().run() 100 | -------------------------------------------------------------------------------- /examples/slider.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import KV, CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.uix.slider import MDSlider, MDSliderHandle, MDSliderValueLabel 6 | 7 | 8 | class Example(MDApp, CommonApp): 9 | def build(self): 10 | self.theme_cls.primary_palette = "Red" 11 | return Builder.load_string(KV) 12 | 13 | def on_start(self): 14 | self.root.ids.widget_box.add_widget( 15 | MDSlider( 16 | MDSliderHandle(), 17 | MDSliderValueLabel(), 18 | step=10, 19 | value=50, 20 | ) 21 | ) 22 | self.root.ids.custom_widget_box.add_widget( 23 | MDSlider( 24 | MDSliderHandle( 25 | theme_bg_color="Custom", 26 | md_bg_color="teal", 27 | state_layer_color="black", 28 | ), 29 | step=10, 30 | value=50, 31 | track_active_color="green", 32 | track_inactive_color="lightgreen", 33 | track_active_step_point_color="white", 34 | track_inactive_step_point_color="green", 35 | ) 36 | ) 37 | 38 | 39 | Example().run() 40 | -------------------------------------------------------------------------------- /examples/switch.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import KV, CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.uix.selectioncontrol import MDSwitch 6 | 7 | 8 | class Example(MDApp, CommonApp): 9 | def build(self): 10 | return Builder.load_string(KV) 11 | 12 | def on_start(self): 13 | self.root.ids.widget_box.add_widget( 14 | MDSwitch( 15 | icon_active="check", 16 | icon_inactive="close", 17 | ) 18 | ) 19 | 20 | self.root.ids.custom_widget_box.add_widget( 21 | MDSwitch( 22 | icon_active="check", 23 | icon_inactive="close", 24 | md_bg_color_disabled="#b5b8b166", 25 | thumb_color_disabled=[1, 0, 1, 0.4], 26 | icon_active_color="white", 27 | icon_inactive_color="red", 28 | thumb_color_active="red", 29 | thumb_color_inactive="white", 30 | track_color_active="brown", 31 | track_color_inactive="teal", 32 | line_color_disabled="darkgrey", 33 | theme_line_color="Custom", 34 | line_color="red", 35 | ) 36 | ) 37 | 38 | 39 | Example().run() 40 | -------------------------------------------------------------------------------- /examples/tab.py: -------------------------------------------------------------------------------- 1 | from kivy.lang import Builder 2 | 3 | from examples.common_app import CommonApp 4 | from kivymd.app import MDApp 5 | from kivymd.uix.label import MDLabel 6 | from kivymd.uix.tab import ( 7 | MDTabsItem, 8 | MDTabsItemIcon, 9 | MDTabsItemSecondary, 10 | MDTabsItemText, 11 | ) 12 | 13 | KV = """ 14 | MDScreen: 15 | md_bg_color: self.theme_cls.backgroundColor 16 | 17 | MDIconButton: 18 | on_release: app.open_menu(self) 19 | pos_hint: {"top": .98} 20 | x: dp(12) 21 | icon: "menu" 22 | 23 | MDBoxLayout: 24 | orientation: "vertical" 25 | adaptive_size: True 26 | pos_hint: {"center_x": .5, "center_y": .5} 27 | size_hint_x: .6 28 | 29 | MDTabsPrimary: 30 | id: primary_tabs 31 | MDDivider: 32 | 33 | MDTabsCarousel: 34 | id: primary_related_content_container 35 | size_hint_y: None 36 | height: dp(140) 37 | 38 | MDTabsSecondary: 39 | id: secondary_tabs 40 | MDDivider: 41 | 42 | MDTabsCarousel: 43 | id: secondary_related_content_container 44 | size_hint_y: None 45 | height: dp(140) 46 | """ 47 | 48 | 49 | class Example(MDApp, CommonApp): 50 | def on_start(self): 51 | for type_tabs in ["primary", "secondary"]: 52 | if type_tabs == "primary": 53 | tabs = self.root.ids.primary_tabs 54 | content_container = ( 55 | self.root.ids.primary_related_content_container 56 | ) 57 | item = MDTabsItem 58 | else: 59 | tabs = self.root.ids.secondary_tabs 60 | content_container = ( 61 | self.root.ids.secondary_related_content_container 62 | ) 63 | item = MDTabsItemSecondary 64 | 65 | for tab_icon, tab_name in { 66 | "airplane": "Flights", 67 | "treasure-chest": "Trips", 68 | "compass-outline": "Explore", 69 | }.items(): 70 | tabs.add_widget( 71 | item( 72 | MDTabsItemIcon( 73 | icon=tab_icon, 74 | ), 75 | MDTabsItemText( 76 | text=tab_name, 77 | ), 78 | ) 79 | ) 80 | content_container.add_widget( 81 | MDLabel( 82 | text=tab_name, 83 | halign="center", 84 | valign="center", 85 | ) 86 | ) 87 | tabs.switch_tab(icon="airplane") 88 | 89 | def build(self): 90 | self.theme_cls.primary_palette = "Olive" 91 | return Builder.load_string(KV) 92 | 93 | def disabled_widgets(self): ... 94 | 95 | 96 | Example().run() 97 | -------------------------------------------------------------------------------- /kivymd/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | KivyMD 3 | ====== 4 | 5 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/previous.png 6 | 7 | Is a collection of Material Design compliant widgets for use with, 8 | `Kivy cross-platform graphical framework `_ 9 | a framework for cross-platform, touch-enabled graphical applications. 10 | The project's goal is to approximate Google's `Material Design spec 11 | `_ as close as possible without 12 | sacrificing ease of use. 13 | 14 | This library is a fork of the `KivyMD project 15 | `_. We found the strength and brought this 16 | project to a new level. 17 | 18 | If you wish to become a project developer (permission to create branches on the 19 | project without forking for easier collaboration), have at least one PR 20 | approved and ask for it. If you contribute regularly to the project the role 21 | may be offered to you without asking too. 22 | """ 23 | 24 | import os 25 | 26 | import kivy 27 | from kivy.logger import Logger 28 | 29 | from kivymd._version import __version__, release 30 | 31 | if "READTHEDOCS" not in os.environ: 32 | kivy.require("2.3.0") 33 | 34 | try: 35 | from kivymd._version import __date__, __hash__, __short_hash__ 36 | except ImportError: 37 | __hash__ = __short_hash__ = __date__ = "" 38 | 39 | path = os.path.dirname(__file__) 40 | """Path to KivyMD package directory.""" 41 | 42 | fonts_path = os.path.join(path, f"fonts{os.sep}") 43 | """Path to fonts directory.""" 44 | 45 | images_path = os.path.join(path, f"images{os.sep}") 46 | """Path to images directory.""" 47 | 48 | uix_path = os.path.join(path, "uix") 49 | """Path to uix directory.""" 50 | 51 | _log_message = ( 52 | "KivyMD:" 53 | + (" Release" if release else "") 54 | + f" {__version__}" 55 | + (f", git-{__short_hash__}" if __short_hash__ else "") 56 | + (f", {__date__}" if __date__ else "") 57 | + f' (installed at "{__file__}")' 58 | ) 59 | Logger.info(_log_message) 60 | 61 | import kivymd.animation # NOQA 62 | import kivymd.factory_registers # NOQA 63 | import kivymd.font_definitions # NOQA 64 | from kivymd.tools.packaging.pyinstaller import hooks_path # NOQA 65 | -------------------------------------------------------------------------------- /kivymd/_version.py: -------------------------------------------------------------------------------- 1 | release = False 2 | __version__ = "2.0.1.dev0" 3 | __hash__ = "f7bde69707ac708a758a02d89f14997ee468d1ee" 4 | __short_hash__ = "f7bde69" 5 | __date__ = "2024-02-27" 6 | -------------------------------------------------------------------------------- /kivymd/effects/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Effects 3 | ======= 4 | """ 5 | -------------------------------------------------------------------------------- /kivymd/effects/stiffscroll/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 LogicalDash 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /kivymd/effects/stiffscroll/README.md: -------------------------------------------------------------------------------- 1 | stiffscroll 2 | =========== 3 | 4 | A ScrollEffect for use with a Kivy ScrollView. It makes scrolling more 5 | laborious as you reach the edge of the scrollable area. 6 | 7 | A ScrollView constructed with StiffScrollEffect, 8 | eg. ScrollView(effect_cls=StiffScrollEffect), will get harder to 9 | scroll as you get nearer to its edges. You can scroll all the way to 10 | the edge if you want to, but it will take more finger-movement than 11 | usual. 12 | 13 | Unlike DampedScrollEffect, it is impossible to overscroll with 14 | StiffScrollEffect. That means you cannot push the contents of the 15 | ScrollView far enough to see what's beneath them. This is appropriate 16 | if the ScrollView contains, eg., a background image, like a desktop 17 | wallpaper. Overscrolling may give the impression that there is some 18 | reason to overscroll, even if just to take a peek beneath, and that 19 | impression may be misleading. 20 | 21 | StiffScrollEffect was written by Zachary Spector. His other stuff is at: 22 | https://github.com/LogicalDash/ 23 | He can be reached, and possibly hired, at: 24 | zacharyspector@gmail.com -------------------------------------------------------------------------------- /kivymd/effects/stiffscroll/__init__.py: -------------------------------------------------------------------------------- 1 | from .stiffscroll import StiffScrollEffect 2 | -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /kivymd/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /kivymd/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /kivymd/images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/images/folder.png -------------------------------------------------------------------------------- /kivymd/images/logo/kivymd-icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/images/logo/kivymd-icon-128.png -------------------------------------------------------------------------------- /kivymd/images/logo/kivymd-icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/images/logo/kivymd-icon-256.png -------------------------------------------------------------------------------- /kivymd/images/logo/kivymd-icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/images/logo/kivymd-icon-512.png -------------------------------------------------------------------------------- /kivymd/images/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/images/transparent.png -------------------------------------------------------------------------------- /kivymd/material_resources.py: -------------------------------------------------------------------------------- 1 | """ 2 | Material Resources 3 | ================== 4 | """ 5 | 6 | import os 7 | 8 | from kivy.core.window import Window 9 | from kivy.metrics import dp 10 | from kivy.utils import platform 11 | 12 | if "KIVY_DOC_INCLUDE" in os.environ: 13 | dp = lambda x: x # NOQA: F811 14 | 15 | # Feel free to override this const if you're designing for a device such as 16 | # a GNU/Linux tablet. 17 | DEVICE_IOS = platform == "ios" or platform == "macosx" 18 | if platform != "android" and platform != "ios": 19 | DEVICE_TYPE = "desktop" 20 | elif Window.width >= dp(600) and Window.height >= dp(600): 21 | DEVICE_TYPE = "tablet" 22 | else: 23 | DEVICE_TYPE = "mobile" 24 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_bg_color_after_changed_theme.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDButton: 10 | id: button 11 | pos_hint: {"center_x": .5, "center_y": .5} 12 | 13 | MDButtonText: 14 | id: button_text 15 | text: "Button" 16 | """ 17 | 18 | 19 | class TestBgColorAfterChangedTheme(MDApp): 20 | def build(self): 21 | return Builder.load_string(KV) 22 | 23 | def check_button_colors(self, *args): 24 | assert self.root.ids.button.md_bg_color == [ 25 | 1.0, 26 | 0.9411764705882353, 27 | 0.9333333333333333, 28 | 1, 29 | ] 30 | assert self.root.ids.button_text.text_color == [ 31 | 0.5647058823529412, 32 | 0.29411764705882354, 33 | 0.25098039215686274, 34 | 1.0, 35 | ] 36 | self.stop() 37 | 38 | def change_palette(self, *args): 39 | self.theme_cls.primary_palette = "Red" 40 | Clock.schedule_once(self.check_button_colors, 1.2) 41 | 42 | def on_start(self): 43 | Clock.schedule_once(self.change_palette, 1.2) 44 | 45 | 46 | TestBgColorAfterChangedTheme().run() 47 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_fab_size_m3_style.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | from kivymd.uix.button import MDFabButton 6 | 7 | KV = """ 8 | MDScreen: 9 | 10 | MDBoxLayout: 11 | id: box 12 | spacing: "56dp" 13 | adaptive_size: True 14 | pos_hint: {"center_x": .5, "center_y": .5} 15 | """ 16 | 17 | 18 | class TestFabSizeM3Style(MDApp): 19 | def build(self): 20 | return Builder.load_string(KV) 21 | 22 | def check_size_button(self, *args): 23 | data = { 24 | "large": [96.0, 96.0], 25 | "small": [40.0, 40.0], 26 | "standard": [56.0, 56.0], 27 | } 28 | for button in self.root.ids.box.children: 29 | assert button.size == data[button.style] 30 | 31 | self.stop() 32 | 33 | def on_start(self): 34 | styles = ["standard", "small", "large"] 35 | for style in styles: 36 | self.root.ids.box.add_widget( 37 | MDFabButton(icon="pencil", style=style) 38 | ) 39 | 40 | Clock.schedule_once(self.check_size_button, 1.2) 41 | 42 | 43 | TestFabSizeM3Style().run() 44 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_icon_button_md_bg_color_disabled.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.button import MDIconButton 5 | from kivymd.uix.screen import MDScreen 6 | 7 | 8 | class TestMdIconButtonMdBgColorDisabled(MDApp): 9 | def build(self): 10 | return MDScreen() 11 | 12 | def generate_buttons(self): 13 | button = MDIconButton( 14 | disabled=True, 15 | md_bg_color_disabled="red", 16 | pos_hint={"center_x": 0.5, "center_y": 0.5}, 17 | ) 18 | self.root.clear_widgets() 19 | self.root.add_widget(button) 20 | 21 | Clock.schedule_once( 22 | lambda y, x=button: self.check_button_md_bg_color_disabled(x), 1 23 | ) 24 | Clock.schedule_once(lambda x: self.stop(), 2) 25 | 26 | def check_button_md_bg_color_disabled(self, button): 27 | assert button.canvas.before.get_group("md-icon-button-bg-color")[ 28 | 0 29 | ].rgba == [1.0, 0.0, 0.0, 1.0] 30 | 31 | def on_start(self): 32 | self.generate_buttons() 33 | 34 | 35 | TestMdIconButtonMdBgColorDisabled().run() 36 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_md_icon_button_icon_size.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDIconButton: 10 | id: button 11 | icon: "language-python" 12 | theme_font_size: "Custom" 13 | font_size: "48sp" 14 | radius: [self.height / 2, ] 15 | size_hint: None, None 16 | size: "84dp", "84dp" 17 | pos_hint: {"center_x": .5, "center_y": .5} 18 | """ 19 | 20 | 21 | class TestMDIconButtonIconSize(MDApp): 22 | def build(self): 23 | return Builder.load_string(KV) 24 | 25 | def check_icon_size(self, *args): 26 | assert self.root.ids.button.width > 48.0 27 | self.stop() 28 | 29 | def on_start(self): 30 | Clock.schedule_once(self.check_icon_size, 1.2) 31 | 32 | 33 | TestMDIconButtonIconSize().run() 34 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_on_release.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | from kivy.properties import BooleanProperty 4 | 5 | from kivymd.app import MDApp 6 | 7 | KV = """ 8 | MDScreen: 9 | 10 | MDButton: 11 | id: button 12 | pos_hint: {"center_x": .5, "center_y": .5} 13 | on_release: app.flag = True 14 | 15 | MDButtonText: 16 | text: "Text" 17 | """ 18 | 19 | 20 | class TestOnRelease(MDApp): 21 | flag = BooleanProperty(False) 22 | 23 | def build(self): 24 | return Builder.load_string(KV) 25 | 26 | def check_release_flag(self, *args): 27 | assert self.flag is True 28 | self.stop() 29 | 30 | def press_button(self, *args): 31 | self.root.ids.button.dispatch("on_release") 32 | Clock.schedule_once(self.check_release_flag, 2) 33 | 34 | def on_start(self): 35 | Clock.schedule_once(self.press_button, 1) 36 | 37 | 38 | TestOnRelease().run() 39 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_outline_button_custom_color.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDButton: 10 | id: button 11 | theme_line_color: "Custom" 12 | line_color: "red" 13 | pos_hint: {"center_x": .5, "center_y": .5} 14 | 15 | MDButtonText: 16 | text: "Button" 17 | """ 18 | 19 | 20 | class TestOutlineButtonCustomColors(MDApp): 21 | def build(self): 22 | return Builder.load_string(KV) 23 | 24 | def check_button_colors(self, *args): 25 | assert self.root.ids.button.line_color == [1.0, 0.0, 0.0, 1.0] 26 | self.stop() 27 | 28 | def on_start(self): 29 | Clock.schedule_once(self.check_button_colors, 1.2) 30 | 31 | 32 | TestOutlineButtonCustomColors().run() 33 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_outline_button_custom_color_after_disabled.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDButton: 10 | id: button 11 | theme_line_color: "Custom" 12 | line_color: "red" 13 | pos_hint: {"center_x": .5, "center_y": .5} 14 | 15 | MDButtonText: 16 | text: "Button" 17 | """ 18 | 19 | 20 | class TestOutlineButtonCustomColorsAfterDisabled(MDApp): 21 | state_button = "unchecked" 22 | 23 | def build(self): 24 | return Builder.load_string(KV) 25 | 26 | def check_button_colors(self, *args): 27 | def check_button_colors_control(dt): 28 | button.disabled = False 29 | self.state_button = "checked" 30 | Clock.schedule_once(self.check_button_colors, 1.2) 31 | 32 | button = self.root.ids.button 33 | assert button.line_color == [1.0, 0.0, 0.0, 1.0] 34 | 35 | if self.state_button == "checked": 36 | self.stop() 37 | elif self.state_button == "unchecked": 38 | button.disabled = True 39 | Clock.schedule_once(check_button_colors_control, 1.2) 40 | 41 | def on_start(self): 42 | Clock.schedule_once(self.check_button_colors, 1.2) 43 | 44 | 45 | TestOutlineButtonCustomColorsAfterDisabled().run() 46 | -------------------------------------------------------------------------------- /kivymd/tests/button/test_size_hint.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.button import MDButton 5 | from kivymd.uix.screen import MDScreen 6 | 7 | 8 | class TestSizeHint(MDApp): 9 | def build(self): 10 | return MDScreen() 11 | 12 | def generate_buttons(self): 13 | button = MDButton( 14 | size_hint_x=1, theme_width="Custom", height=self.root.height 15 | ) 16 | self.root.clear_widgets() 17 | self.root.add_widget(button) 18 | Clock.schedule_once(lambda x: self.check_button_size(button), 1) 19 | 20 | def check_button_size(self, button): 21 | assert button.size == [800, 600] 22 | self.stop() 23 | 24 | def on_start(self): 25 | self.generate_buttons() 26 | 27 | 28 | TestSizeHint().run() 29 | -------------------------------------------------------------------------------- /kivymd/tests/data/danger.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tests/data/danger.ttf -------------------------------------------------------------------------------- /kivymd/tests/hero/test_with_first_screen_without_hero.py: -------------------------------------------------------------------------------- 1 | # Test for https://github.com/kivymd/KivyMD/issues/1412 issue. 2 | 3 | from kivy.clock import Clock 4 | from kivy.lang import Builder 5 | 6 | from kivymd.app import MDApp 7 | from kivymd.uix.screen import MDScreen 8 | 9 | 10 | class ScreenWithoutHero(MDScreen): 11 | """ 12 | This is the first screen from which we go to the screen that contains hero. 13 | """ 14 | 15 | 16 | class ScreenWithHeroFrom(MDScreen): 17 | """This is the screen that contains hero.""" 18 | 19 | 20 | class ScreenWithHeroTo(MDScreen): 21 | """This is the screen where the hero moves""" 22 | 23 | 24 | KV = """ 25 | 26 | name: "Screen Without Hero" 27 | 28 | 29 | 30 | name: "Screen With Hero From" 31 | 32 | MDHeroFrom: 33 | id: hero_from 34 | tag: "hero" 35 | size_hint: None, None 36 | size: "120dp", "120dp" 37 | pos_hint: {"top": .98} 38 | x: 24 39 | 40 | FitImage: 41 | source: "kivymd/images/logo/kivymd-icon-256.png" 42 | size_hint: None, None 43 | size: hero_from.size 44 | 45 | 46 | name: "Screen With Hero To" 47 | heroes_to: [hero_to] 48 | 49 | MDHeroTo: 50 | id: hero_to 51 | tag: "hero" 52 | size_hint: None, None 53 | size: "220dp", "220dp" 54 | pos_hint: {"center_x": .5, "center_y": .5} 55 | 56 | 57 | MDScreenManager: 58 | 59 | ScreenWithoutHero: 60 | 61 | ScreenWithHeroFrom: 62 | 63 | ScreenWithHeroTo: 64 | """ 65 | 66 | 67 | class TestWithFirstScreenWithoutHero(MDApp): 68 | def build(self): 69 | return Builder.load_string(KV) 70 | 71 | def move_hero_to_another_screen(self, *args): 72 | self.root.transition._direction = "in" 73 | self.root.current_heroes = ["hero"] 74 | self.root.current = "Screen With Hero To" 75 | Clock.schedule_once(self.move_hero_to_previous_screen, 1) 76 | 77 | def move_hero_to_previous_screen(self, *args): 78 | self.root.current_heroes = ["hero"] 79 | self.root.current = "Screen With Hero From" 80 | Clock.schedule_once(self.set_screen_without_hero, 1) 81 | 82 | def set_screen_without_hero(self, *args): 83 | self.root.current_heroes = [] 84 | self.root.current = "Screen Without Hero" 85 | Clock.schedule_once(lambda x: self.stop(), 1) 86 | 87 | def set_screen_with_hero_from(self, *args): 88 | self.root.current = "Screen With Hero From" 89 | Clock.schedule_once(self.move_hero_to_another_screen, 1) 90 | 91 | def on_start(self): 92 | Clock.schedule_once(self.set_screen_with_hero_from, 1) 93 | 94 | 95 | TestWithFirstScreenWithoutHero().run() 96 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_allow_copy.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.core.clipboard import Clipboard 3 | from kivy.input.providers.mouse import MouseMotionEvent 4 | from kivy.lang.builder import Builder 5 | 6 | from kivymd.app import MDApp 7 | 8 | KV = """ 9 | MDScreen: 10 | 11 | MDLabel: 12 | id: label 13 | halign: "center" 14 | text: "MDLabel" 15 | allow_copy: True 16 | on_copy: app.check_clipboard() 17 | """ 18 | 19 | 20 | class TestAllowCopy(MDApp): 21 | def build(self): 22 | return Builder.load_string(KV) 23 | 24 | def check_clipboard(self): 25 | assert self.root.ids.label.text == Clipboard.paste() 26 | self.stop() 27 | 28 | def on_start(self): 29 | def on_start(*args): 30 | touch = MouseMotionEvent("mouse", "button", self.root.ids.label.pos) 31 | self.root.ids.label.on_double_tap(touch, ()) 32 | 33 | Clock.schedule_once(on_start, 1) 34 | 35 | 36 | TestAllowCopy().run() 37 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_color_deselection.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.input.providers.mouse import MouseMotionEvent 3 | from kivy.lang.builder import Builder 4 | 5 | from kivymd.app import MDApp 6 | 7 | KV = """ 8 | MDScreen: 9 | md_bg_color: self.theme_cls.backgroundColor 10 | 11 | MDLabel: 12 | id: label 13 | halign: "center" 14 | text: "MDLabel" 15 | allow_selection: True 16 | """ 17 | 18 | 19 | class TestColorDeselection(MDApp): 20 | def build(self): 21 | return Builder.load_string(KV) 22 | 23 | def check_selection(self, *args): 24 | self.root.ids.label.cancel_selection() 25 | assert self.root.ids.label.md_bg_color == self.theme_cls.backgroundColor 26 | self.stop() 27 | 28 | def on_start(self): 29 | self.touch = MouseMotionEvent( 30 | "mouse", "button", self.root.ids.label.pos 31 | ) 32 | self.root.ids.label.on_double_tap(self.touch, ()) 33 | Clock.schedule_once(self.check_selection) 34 | 35 | 36 | TestColorDeselection().run() 37 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_color_selection.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.input.providers.mouse import MouseMotionEvent 3 | from kivy.lang.builder import Builder 4 | 5 | from kivymd.app import MDApp 6 | 7 | KV = """ 8 | MDScreen: 9 | 10 | MDLabel: 11 | id: label 12 | halign: "center" 13 | text: "MDLabel" 14 | allow_selection: True 15 | color_selection: "red" 16 | """ 17 | 18 | 19 | class TestColorSelection(MDApp): 20 | def build(self): 21 | self.theme_cls.primary_palette = "Red" 22 | return Builder.load_string(KV) 23 | 24 | def check_selection(self, *args): 25 | assert self.root.ids.label.md_bg_color == [1.0, 0.0, 0.0, 1.0] 26 | self.stop() 27 | 28 | def on_start(self): 29 | touch = MouseMotionEvent("mouse", "button", self.root.ids.label.pos) 30 | self.root.ids.label.on_double_tap(touch, ()) 31 | Clock.schedule_once(self.check_selection) 32 | 33 | 34 | TestColorSelection().run() 35 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_default_theme_text_color.py: -------------------------------------------------------------------------------- 1 | import asynckivy 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.label import MDLabel 5 | from kivymd.uix.screen import MDScreen 6 | 7 | 8 | class TestDefaultThemeTextColor(MDApp): 9 | def __init__(self, **kwargs): 10 | super().__init__(**kwargs) 11 | self.color_data = { 12 | "Secondary": getattr(self.theme_cls, "secondaryColor"), 13 | "Hint": getattr(self.theme_cls, "disabledTextColor"), 14 | "Error": getattr(self.theme_cls, "errorColor"), 15 | } 16 | 17 | def build(self): 18 | return MDScreen() 19 | 20 | async def generate_labels(self): 21 | for name_color in self.color_data.keys(): 22 | await asynckivy.sleep(0) 23 | label = MDLabel( 24 | text="MDLabel", text_color=self.color_data[name_color] 25 | ) 26 | self.root.add_widget(label) 27 | assert label.color == self.color_data[name_color] 28 | self.root.clear_widgets() 29 | self.stop() 30 | 31 | def on_start(self): 32 | asynckivy.start(self.generate_labels()) 33 | 34 | 35 | TestDefaultThemeTextColor().run() 36 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_disabled_color.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang.builder import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDLabel: 10 | id: label 11 | halign: "center" 12 | text: "MDLabel" 13 | disabled: True 14 | """ 15 | 16 | 17 | class TestDisabledColor(MDApp): 18 | def build(self): 19 | return Builder.load_string(KV) 20 | 21 | def check_disabled_color(self, *args): 22 | assert ( 23 | self.root.ids.label.disabled_color 24 | == self.theme_cls.onSurfaceColor[:-1] 25 | + [self.root.ids.label.label_opacity_value_disabled_text] 26 | ) 27 | self.stop() 28 | 29 | def on_start(self): 30 | Clock.schedule_once(self.check_disabled_color, 1) 31 | 32 | 33 | TestDisabledColor().run() 34 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_font_size_in_python_code.py: -------------------------------------------------------------------------------- 1 | # Test for https://github.com/kivymd/KivyMD/issues/1435: 2 | # 3 | # Test task: 4 | # 5 | # - check the size of the custom font that was installed in the Python code; 6 | 7 | from kivy.clock import Clock 8 | 9 | from kivymd.app import MDApp 10 | from kivymd.uix.boxlayout import MDBoxLayout 11 | from kivymd.uix.label import MDLabel 12 | 13 | 14 | class TestFontSizeInPythonCode(MDApp): 15 | def check_font_size(self, *args): 16 | assert self.root.get_ids().label.font_size == 57.0 17 | self.stop() 18 | 19 | def build(self): 20 | return MDBoxLayout( 21 | MDLabel( 22 | id="label", 23 | text="MDLabel", 24 | font_style="Display", 25 | role="large", 26 | ), 27 | ) 28 | 29 | def on_start(self): 30 | Clock.schedule_once(self.check_font_size, 1.2) 31 | 32 | 33 | TestFontSizeInPythonCode().run() 34 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_font_style.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from kivy.clock import Clock 4 | from kivy.core.text import LabelBase 5 | from kivy.lang import Builder 6 | from kivy.metrics import sp 7 | 8 | from kivymd.app import MDApp 9 | 10 | KV = """ 11 | MDScreen: 12 | 13 | MDLabel: 14 | id: label 15 | text: "Danger" 16 | font_style: "Danger" 17 | role: "large" 18 | halign: "center" 19 | font_size: "66sp" 20 | """ 21 | 22 | 23 | class TestFontStyle(MDApp): 24 | def build(self): 25 | LabelBase.register( 26 | name="Danger", 27 | fn_regular=os.path.join( 28 | os.path.dirname(__file__).split("label")[0], 29 | "data", 30 | "danger.ttf", 31 | ), 32 | ) 33 | 34 | self.theme_cls.font_styles["Danger"] = { 35 | "large": { 36 | "line-height": 1.64, 37 | "font-name": "Danger", 38 | "font-size": sp(57), 39 | }, 40 | "medium": { 41 | "line-height": 1.52, 42 | "font-name": "Danger", 43 | "font-size": sp(45), 44 | }, 45 | "small": { 46 | "line-height": 1.44, 47 | "font-name": "Danger", 48 | "font-size": sp(36), 49 | }, 50 | } 51 | return Builder.load_string(KV) 52 | 53 | def on_start(self): 54 | def on_start(*args): 55 | assert ( 56 | sp(57) 57 | == self.theme_cls.font_styles["Danger"][ 58 | self.root.ids.label.role 59 | ]["font-size"] 60 | ) 61 | self.stop() 62 | 63 | Clock.schedule_once(on_start, 2) 64 | 65 | 66 | TestFontStyle().run() 67 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_md_icon_badge_colors.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDIcon: 10 | icon: "mail" 11 | pos_hint: {"center_x": .5, "center_y": .5} 12 | 13 | MDBadge: 14 | id: badge 15 | text: "12" 16 | """ 17 | 18 | 19 | class TestMDIconBadgeColors(MDApp): 20 | def build(self): 21 | return Builder.load_string(KV) 22 | 23 | def check_badge_colors(self, *args): 24 | assert self.root.ids.badge.text_color == self.theme_cls.onErrorColor 25 | assert self.root.ids.badge.md_bg_color == self.theme_cls.errorColor 26 | 27 | self.stop() 28 | 29 | def on_start(self): 30 | Clock.schedule_once(self.check_badge_colors, 2) 31 | 32 | 33 | TestMDIconBadgeColors().run() 34 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_md_icon_badge_colors_after_disabled.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDIcon: 10 | id: icon 11 | icon: "mail" 12 | pos_hint: {"center_x": .5, "center_y": .5} 13 | 14 | MDBadge: 15 | id: badge 16 | text: "12" 17 | """ 18 | 19 | 20 | class TestMDIconBadgeColorsAfterDisabled(MDApp): 21 | def build(self): 22 | return Builder.load_string(KV) 23 | 24 | def icon_undisabled(self, *args): 25 | self.root.ids.icon.disabled = False 26 | 27 | def check_badge_colors(self, *args): 28 | assert self.root.ids.badge.text_color == self.theme_cls.onErrorColor 29 | assert self.root.ids.badge.md_bg_color == self.theme_cls.errorColor 30 | 31 | self.stop() 32 | 33 | def on_start(self): 34 | Clock.schedule_once(self.icon_undisabled, 1) 35 | Clock.schedule_once(self.check_badge_colors, 2) 36 | 37 | 38 | TestMDIconBadgeColorsAfterDisabled().run() 39 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_switch_theme.py: -------------------------------------------------------------------------------- 1 | # Test for https://github.com/kivymd/KivyMD/issues/1403 issue. 2 | 3 | from kivy.clock import Clock 4 | from kivy.lang import Builder 5 | 6 | from kivymd.app import MDApp 7 | 8 | UI = """ 9 | MDScreenManager: 10 | 11 | MDScreen: 12 | name: "screen one" 13 | 14 | MDLabel: 15 | text: "This is MDLabel" 16 | halign: "center" 17 | 18 | MDScreen: 19 | name: "screen two" 20 | on_enter: app.callback_change_theme() 21 | 22 | MDLabel: 23 | id: label_two 24 | text: "This is MDLabel" 25 | halign: "center" 26 | """ 27 | 28 | 29 | class TestSwitchTheme(MDApp): 30 | def check_label_color(self, *args): 31 | assert self.root.ids.label_two.color == self.theme_cls.onSurfaceColor 32 | self.stop() 33 | 34 | def callback_change_theme(self): 35 | self.theme_cls.theme_style = "Dark" 36 | Clock.schedule_once(self.check_label_color, 1.2) 37 | 38 | def switch_screen_two(self, *args): 39 | self.root.current = "screen two" 40 | 41 | def on_start(self): 42 | Clock.schedule_once(self.switch_screen_two, 1) 43 | 44 | def build(self): 45 | return Builder.load_string(UI) 46 | 47 | 48 | TestSwitchTheme().run() 49 | -------------------------------------------------------------------------------- /kivymd/tests/label/test_theme_text_color.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | 9 | MDLabel: 10 | id: label 11 | text: "MDLabel" 12 | halign: "center" 13 | theme_text_color: "Custom" 14 | text_color: "red" 15 | """ 16 | 17 | 18 | class TestThemeTextColor(MDApp): 19 | def build(self): 20 | return Builder.load_string(KV) 21 | 22 | def on_start(self): 23 | def on_start(*args): 24 | assert self.root.ids.label.text_color == [1.0, 0.0, 0.0, 1.0] 25 | self.stop() 26 | 27 | Clock.schedule_once(on_start, 2) 28 | 29 | 30 | TestThemeTextColor().run() 31 | -------------------------------------------------------------------------------- /kivymd/tests/list/test_disable.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.core.clipboard import Clipboard 3 | from kivy.input.providers.mouse import MouseMotionEvent 4 | from kivy.lang.builder import Builder 5 | 6 | from kivymd.app import MDApp 7 | 8 | KV = """ 9 | MDScreen: 10 | 11 | MDBoxLayout: 12 | orientation: 'vertical' 13 | 14 | MDListItem: 15 | id: item1 16 | 17 | MDListItemLeadingIcon: 18 | icon: "account" 19 | 20 | MDListItemHeadlineText: 21 | text: "Headline1" 22 | 23 | MDListItemSupportingText: 24 | text: "Supporting text" 25 | 26 | MDListItemTertiaryText: 27 | text: "Tertiary text" 28 | 29 | MDListItemTrailingCheckbox: 30 | 31 | MDListItem: 32 | id: item2 33 | 34 | MDListItemHeadlineText: 35 | text: "Headline2" 36 | 37 | MDListItemSupportingText: 38 | text: "Supporting text" 39 | 40 | MDListItemTertiaryText: 41 | text: "Tertiary text" 42 | 43 | MDListItem: 44 | id: item3 45 | 46 | MDListItemHeadlineText: 47 | text: "Headline3" 48 | 49 | MDListItem: 50 | id: item4 51 | 52 | """ 53 | 54 | 55 | class TestDisableList(MDApp): 56 | def build(self): 57 | return Builder.load_string(KV) 58 | 59 | def on_start(self): 60 | def _enabled(*args): 61 | self.root.ids.item1.disabled = False 62 | self.root.ids.item2.disabled = False 63 | self.root.ids.item3.disabled = False 64 | self.root.ids.item4.disabled = False 65 | self.stop() 66 | 67 | def _disable(*args): 68 | self.root.ids.item1.disabled = True 69 | self.root.ids.item2.disabled = True 70 | self.root.ids.item3.disabled = True 71 | self.root.ids.item4.disabled = True 72 | Clock.schedule_once(_enabled, 1) 73 | 74 | Clock.schedule_once(_disable, 1) 75 | 76 | 77 | TestDisableList().run() 78 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_button.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.button import MDButton, MDButtonIcon, MDButtonText 5 | from kivymd.uix.screen import MDScreen 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestButtonMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 0.5) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget( 35 | MDButton( 36 | MDButtonIcon(icon="plus"), 37 | MDButtonText(text=f"Count {self.counter}"), 38 | ) 39 | ) 40 | 41 | if self.counter > 10: 42 | Clock.unschedule(self.add_items) 43 | assert len_callbacks == self.previous_len_callbacks 44 | self.stop() 45 | 46 | 47 | TestButtonMemoryLeak().run() 48 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_chip.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.chip import MDChip, MDChipLeadingIcon, MDChipText 5 | from kivymd.uix.screen import MDScreen 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestButtonMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 0.5) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget( 35 | MDChip( 36 | MDChipLeadingIcon( 37 | icon="account", 38 | ), 39 | MDChipText( 40 | text="MDChipText", 41 | ), 42 | ) 43 | ) 44 | 45 | if self.counter > 10: 46 | Clock.unschedule(self.add_items) 47 | assert len_callbacks == self.previous_len_callbacks 48 | self.stop() 49 | 50 | 51 | TestButtonMemoryLeak().run() 52 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_exdented_fab_button.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.button import ( 5 | MDExtendedFabButton, 6 | MDExtendedFabButtonIcon, 7 | MDExtendedFabButtonText, 8 | ) 9 | from kivymd.uix.screen import MDScreen 10 | 11 | len_callbacks = 0 12 | 13 | 14 | class MyScreen(MDScreen): 15 | def remove_widget(self, *args, **kwargs) -> None: 16 | global len_callbacks 17 | 18 | super().remove_widget(*args, **kwargs) 19 | len_callbacks = len( 20 | self.theme_cls.get_property_observers("theme_style") 21 | ) 22 | 23 | 24 | class TestButtonMemoryLeak(MDApp): 25 | counter = 0 26 | previous_len_callbacks = 0 27 | 28 | def build(self): 29 | Clock.schedule_interval(self.add_items, 0.5) 30 | return MyScreen() 31 | 32 | def add_items(self, *args): 33 | if len_callbacks: 34 | self.previous_len_callbacks = len_callbacks 35 | 36 | self.counter += 1 37 | self.root.clear_widgets() 38 | self.root.add_widget( 39 | MDExtendedFabButton( 40 | MDExtendedFabButtonIcon( 41 | icon="pencil-outline", 42 | ), 43 | MDExtendedFabButtonText( 44 | text="Compose", 45 | ), 46 | fab_state="expand", 47 | ) 48 | ) 49 | 50 | if self.counter > 10: 51 | Clock.unschedule(self.add_items) 52 | assert len_callbacks == self.previous_len_callbacks 53 | self.stop() 54 | 55 | 56 | TestButtonMemoryLeak().run() 57 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_fab_button.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.button import MDFabButton 5 | from kivymd.uix.screen import MDScreen 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestButtonMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 0.5) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget(MDFabButton(icon="plus")) 35 | 36 | if self.counter > 10: 37 | Clock.unschedule(self.add_items) 38 | assert len_callbacks == self.previous_len_callbacks 39 | self.stop() 40 | 41 | 42 | TestButtonMemoryLeak().run() 43 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_icon_button.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.button import MDIconButton 5 | from kivymd.uix.screen import MDScreen 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestButtonMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 0.5) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget(MDIconButton(icon="plus")) 35 | 36 | if self.counter > 10: 37 | Clock.unschedule(self.add_items) 38 | assert len_callbacks == self.previous_len_callbacks 39 | self.stop() 40 | 41 | 42 | TestButtonMemoryLeak().run() 43 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_label.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.label import MDLabel 5 | from kivymd.uix.screen import MDScreen 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestLabelMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 1) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget( 35 | MDLabel(text=f"Count {self.counter}", halign="center") 36 | ) 37 | 38 | if self.counter > 10: 39 | Clock.unschedule(self.add_items) 40 | assert len_callbacks == self.previous_len_callbacks 41 | self.stop() 42 | 43 | 44 | TestLabelMemoryLeak().run() 45 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_slider.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.screen import MDScreen 5 | from kivymd.uix.slider import MDSlider, MDSliderHandle, MDSliderValueLabel 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestButtonMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 0.5) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget( 35 | MDSlider( 36 | MDSliderHandle(), 37 | MDSliderValueLabel(), 38 | step=10, 39 | value=50, 40 | ) 41 | ) 42 | 43 | if self.counter > 10: 44 | Clock.unschedule(self.add_items) 45 | assert len_callbacks == self.previous_len_callbacks 46 | self.stop() 47 | 48 | 49 | TestButtonMemoryLeak().run() 50 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_switch.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.screen import MDScreen 5 | from kivymd.uix.selectioncontrol import MDSwitch 6 | 7 | len_callbacks = 0 8 | 9 | 10 | class MyScreen(MDScreen): 11 | def remove_widget(self, *args, **kwargs) -> None: 12 | global len_callbacks 13 | 14 | super().remove_widget(*args, **kwargs) 15 | len_callbacks = len( 16 | self.theme_cls.get_property_observers("theme_style") 17 | ) 18 | 19 | 20 | class TestButtonMemoryLeak(MDApp): 21 | counter = 0 22 | previous_len_callbacks = 0 23 | 24 | def build(self): 25 | Clock.schedule_interval(self.add_items, 0.5) 26 | return MyScreen() 27 | 28 | def add_items(self, *args): 29 | if len_callbacks: 30 | self.previous_len_callbacks = len_callbacks 31 | 32 | self.counter += 1 33 | self.root.clear_widgets() 34 | self.root.add_widget( 35 | MDSwitch( 36 | icon_active="check", 37 | icon_inactive="close", 38 | ) 39 | ) 40 | 41 | if self.counter > 10: 42 | Clock.unschedule(self.add_items) 43 | assert len_callbacks == self.previous_len_callbacks 44 | self.stop() 45 | 46 | 47 | TestButtonMemoryLeak().run() 48 | -------------------------------------------------------------------------------- /kivymd/tests/memory/test_textfield.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | 3 | from kivymd.app import MDApp 4 | from kivymd.uix.screen import MDScreen 5 | from kivymd.uix.textfield import ( 6 | MDTextField, 7 | MDTextFieldHelperText, 8 | MDTextFieldHintText, 9 | MDTextFieldLeadingIcon, 10 | MDTextFieldMaxLengthText, 11 | MDTextFieldTrailingIcon, 12 | ) 13 | 14 | len_callbacks = 0 15 | 16 | 17 | class MyScreen(MDScreen): 18 | def __init__(self, *args, **kwargs): 19 | super().__init__(*args, **kwargs) 20 | self.md_bg_color = self.theme_cls.backgroundColor 21 | 22 | def remove_widget(self, *args, **kwargs) -> None: 23 | global len_callbacks 24 | 25 | super().remove_widget(*args, **kwargs) 26 | len_callbacks = len( 27 | self.theme_cls.get_property_observers("theme_style") 28 | ) 29 | 30 | 31 | class TestTextFieldMemoryLeak(MDApp): 32 | counter = 0 33 | previous_len_callbacks = 0 34 | 35 | def build(self): 36 | Clock.schedule_interval(self.add_items, 0.5) 37 | return MyScreen() 38 | 39 | def add_items(self, *args): 40 | if len_callbacks: 41 | self.previous_len_callbacks = len_callbacks 42 | 43 | self.counter += 1 44 | self.root.clear_widgets() 45 | self.root.add_widget( 46 | MDTextField( 47 | MDTextFieldLeadingIcon( 48 | icon="account", 49 | ), 50 | MDTextFieldHintText( 51 | text="Hint text", 52 | ), 53 | MDTextFieldHelperText( 54 | text="Helper text", 55 | mode="persistent", 56 | ), 57 | MDTextFieldTrailingIcon( 58 | icon="information", 59 | ), 60 | MDTextFieldMaxLengthText( 61 | max_text_length=10, 62 | ), 63 | ) 64 | ) 65 | 66 | if self.counter > 10: 67 | Clock.unschedule(self.add_items) 68 | assert len_callbacks == self.previous_len_callbacks 69 | self.stop() 70 | 71 | 72 | TestTextFieldMemoryLeak().run() 73 | -------------------------------------------------------------------------------- /kivymd/tests/pyinstaller/test_pyinstaller_packaging.py: -------------------------------------------------------------------------------- 1 | """ 2 | PyInstaller freezing test 3 | ========================= 4 | 5 | PyInstaller must package KivyMD apps correctly. 6 | """ 7 | 8 | import subprocess 9 | 10 | from PyInstaller import __main__ as pyi_main 11 | 12 | 13 | def test_datas(tmp_path) -> None: 14 | """Test fonts and images.""" 15 | 16 | app_name = "userapp" 17 | workpath = tmp_path / "build" 18 | distpath = tmp_path / "dist" 19 | app = tmp_path / (app_name + ".py") 20 | app.write_text( 21 | """ 22 | import os 23 | 24 | from kivy.core.text import LabelBase 25 | 26 | import kivymd 27 | 28 | fonts = os.listdir(kivymd.fonts_path) 29 | print(fonts) 30 | assert "Roboto-Regular.ttf" in fonts 31 | assert "materialdesignicons-webfont.ttf" in fonts 32 | print(LabelBase._fonts.keys()) 33 | assert "Roboto" in LabelBase._fonts.keys() # NOQA 34 | assert "Icons" in LabelBase._fonts.keys() # NOQA 35 | 36 | images = os.listdir(kivymd.images_path) 37 | print(images) 38 | assert "logo" in images 39 | assert "folder.png" in images 40 | assert "transparent.png" in images 41 | """ 42 | ) 43 | pyi_main.run( 44 | [ 45 | "--workpath", 46 | str(workpath), 47 | "--distpath", 48 | str(distpath), 49 | "--specpath", 50 | str(tmp_path), 51 | str(app), 52 | ] 53 | ) 54 | subprocess.run([str(distpath / app_name / app_name)], check=True) 55 | 56 | 57 | def test_widgets(tmp_path) -> None: 58 | """Test that all widgets are accesible.""" 59 | 60 | app_name = "userapp" 61 | workpath = tmp_path / "build" 62 | distpath = tmp_path / "dist" 63 | app = tmp_path / (app_name + ".py") 64 | app.write_text( 65 | """ 66 | import os 67 | 68 | import kivymd # NOQA 69 | __import__("kivymd.uix.label") 70 | __import__("kivymd.uix.button") 71 | __import__("kivymd.uix.list") 72 | __import__("kivymd.uix.navigationdrawer") 73 | 74 | print(os.listdir(os.path.dirname(kivymd.uix.__path__[0]))) 75 | """ 76 | ) 77 | pyi_main.run( 78 | [ 79 | "--workpath", 80 | str(workpath), 81 | "--distpath", 82 | str(distpath), 83 | "--specpath", 84 | str(tmp_path), 85 | str(app), 86 | ] 87 | ) 88 | subprocess.run([str(distpath / app_name / app_name)], check=True) 89 | -------------------------------------------------------------------------------- /kivymd/tests/test_app.py: -------------------------------------------------------------------------------- 1 | from kivy import lang 2 | from kivy.clock import Clock 3 | from kivy.tests.common import GraphicUnitTest 4 | 5 | from kivymd.app import MDApp 6 | from kivymd.theming import ThemeManager 7 | 8 | 9 | class AppTest(GraphicUnitTest): 10 | def test_start_raw_app(self): 11 | lang._delayed_start = None 12 | a = MDApp() 13 | Clock.schedule_once(a.stop, 0.1) 14 | a.run() 15 | 16 | def test_theme_manager_existance(self): 17 | lang._delayed_start = None 18 | a = MDApp() 19 | Clock.schedule_once(a.stop, 0.1) 20 | a.run() 21 | assert isinstance(a.theme_cls, ThemeManager) 22 | -------------------------------------------------------------------------------- /kivymd/tests/test_font_definitions.py: -------------------------------------------------------------------------------- 1 | def test_fonts_registration(): 2 | # This should register fonts: 3 | from kivy.core.text import LabelBase 4 | 5 | import kivymd # NOQA 6 | 7 | fonts = [ 8 | "Roboto", 9 | "RobotoThin", 10 | "RobotoLight", 11 | "RobotoMedium", 12 | "RobotoBlack", 13 | "Icons", 14 | ] 15 | for font in fonts: 16 | assert font in LabelBase._fonts.keys() 17 | -------------------------------------------------------------------------------- /kivymd/tests/test_icon_definitions.py: -------------------------------------------------------------------------------- 1 | def test_icons_have_size(): 2 | from kivy.core.text import Label 3 | 4 | from kivymd.icon_definitions import md_icons 5 | 6 | lbl = Label(font_name="Icons") 7 | for icon_name, icon_value in md_icons.items(): 8 | assert len(icon_value) == 1 9 | lbl.refresh() 10 | assert lbl.get_extents(icon_value) is not None 11 | -------------------------------------------------------------------------------- /kivymd/tests/test_md_bg_color_layouts.py: -------------------------------------------------------------------------------- 1 | import asynckivy 2 | from kivy.clock import Clock 3 | from kivy.graphics import Color 4 | 5 | from kivymd.app import MDApp 6 | from kivymd.uix.anchorlayout import MDAnchorLayout 7 | from kivymd.uix.boxlayout import MDBoxLayout 8 | from kivymd.uix.floatlayout import MDFloatLayout 9 | from kivymd.uix.gridlayout import MDGridLayout 10 | from kivymd.uix.recyclegridlayout import MDRecycleGridLayout 11 | from kivymd.uix.recycleview import MDRecycleView 12 | from kivymd.uix.relativelayout import MDRelativeLayout 13 | from kivymd.uix.screen import MDScreen 14 | from kivymd.uix.scrollview import MDScrollView 15 | from kivymd.uix.stacklayout import MDStackLayout 16 | from kivymd.uix.widget import MDWidget 17 | 18 | 19 | class TestMdBgColorLayouts(MDApp): 20 | def build(self): 21 | return MDScreen() 22 | 23 | async def generate_layouts(self): 24 | for layout in [ 25 | MDBoxLayout, 26 | MDRelativeLayout, 27 | MDWidget, 28 | MDStackLayout, 29 | MDScrollView, 30 | MDScreen, 31 | MDRecycleGridLayout, 32 | MDRecycleView, 33 | MDGridLayout, 34 | MDFloatLayout, 35 | MDAnchorLayout, 36 | ]: 37 | await asynckivy.sleep(0) 38 | layout = layout(md_bg_color="red") 39 | self.root.clear_widgets() 40 | self.root.add_widget(layout) 41 | Clock.schedule_once(lambda x: self.check_md_bg_color(layout), 2) 42 | 43 | self.stop() 44 | 45 | def on_start(self): 46 | asynckivy.start(self.generate_layouts()) 47 | 48 | def check_md_bg_color(self, widget): 49 | assert widget.canvas.get_group("backgroundcolor-behavior-bg-color")[ 50 | 0 51 | ].rgba == [1.0, 0.0, 0.0, 1.0] 52 | 53 | 54 | TestMdBgColorLayouts().run() 55 | -------------------------------------------------------------------------------- /kivymd/tests/textfield/test_disabled_text_color.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.backgroundColor 9 | 10 | MDTextField: 11 | id: field 12 | pos_hint: {"center_x": .5, "center_y": .5} 13 | size_hint_x: .6 14 | disabled: True 15 | 16 | MDTextFieldLeadingIcon: 17 | icon: "account" 18 | 19 | MDTextFieldHintText: 20 | text: "Hint text" 21 | 22 | MDTextFieldHelperText: 23 | text: "Helper text" 24 | mode: "persistent" 25 | 26 | MDTextFieldTrailingIcon: 27 | icon: "information" 28 | 29 | MDTextFieldMaxLengthText: 30 | max_text_length: 10 31 | """ 32 | 33 | 34 | class TestDisabledTextColor(MDApp): 35 | def build(self): 36 | return Builder.load_string(KV) 37 | 38 | def check_colors(self, *args): 39 | for group_name in [ 40 | "helper-text-color", 41 | "leading-icons-color", 42 | "trailing-icons-color", 43 | "max-length-color", 44 | ]: 45 | group = self.root.ids.field.canvas.before.get_group(group_name)[0] 46 | assert group.rgba == self.theme_cls.disabledTextColor[:-1] + [0.60] 47 | 48 | group = self.root.ids.field.canvas.after.get_group("hint-text-color")[0] 49 | assert group.rgba == self.theme_cls.disabledTextColor[:-1] + [0.60] 50 | self.stop() 51 | 52 | def on_start(self): 53 | Clock.schedule_once(self.check_colors, 2) 54 | 55 | 56 | TestDisabledTextColor().run() 57 | -------------------------------------------------------------------------------- /kivymd/tests/textfield/test_disabled_text_color_switch_theme.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.backgroundColor 9 | on_touch_down: field.disabled = not field.disabled 10 | 11 | MDTextField: 12 | id: field 13 | pos_hint: {"center_x": .5, "center_y": .5} 14 | size_hint_x: .6 15 | disabled: True 16 | 17 | MDTextFieldLeadingIcon: 18 | icon: "account" 19 | 20 | MDTextFieldHintText: 21 | text: "Hint text" 22 | 23 | MDTextFieldHelperText: 24 | text: "Helper text" 25 | mode: "persistent" 26 | 27 | MDTextFieldTrailingIcon: 28 | icon: "information" 29 | 30 | MDTextFieldMaxLengthText: 31 | max_text_length: 10 32 | """ 33 | 34 | 35 | class TestDisabledTextColorSwitchTheme(MDApp): 36 | def build(self): 37 | return Builder.load_string(KV) 38 | 39 | def check_colors(self, *args): 40 | for group_name in [ 41 | "helper-text-color", 42 | "leading-icons-color", 43 | "trailing-icons-color", 44 | "max-length-color", 45 | ]: 46 | group = self.root.ids.field.canvas.before.get_group(group_name)[0] 47 | assert group.rgba, self.theme_cls.disabledTextColor[:-1] + [0.60] 48 | 49 | group = self.root.ids.field.canvas.after.get_group("hint-text-color")[0] 50 | assert group.rgba == self.theme_cls.disabledTextColor[:-1] + [0.60] 51 | self.stop() 52 | 53 | def change_theme(self, *args): 54 | self.theme_cls.theme_style = "Dark" 55 | Clock.schedule_once(self.check_colors, 3) 56 | 57 | def on_start(self): 58 | Clock.schedule_once(self.change_theme, 2) 59 | 60 | 61 | TestDisabledTextColorSwitchTheme().run() 62 | -------------------------------------------------------------------------------- /kivymd/tests/textfield/test_error_state.py: -------------------------------------------------------------------------------- 1 | # Test task: 2 | # 3 | # The text field is in an error state due to the fact that the length 4 | # of the text exceeds the set value in the `max_text_length` parameter. 5 | # 6 | # - set the focus on the text field; 7 | # - delete text characters up to the set value in the `max_text_length` 8 | # parameter; 9 | # - check the color of the text; 10 | # - check the color of the hint text; 11 | # - check the color of the helper text; 12 | # - check the color of the right icon; 13 | # - check the color of the text max length; 14 | 15 | from kivy.clock import Clock 16 | from kivy.lang import Builder 17 | 18 | from kivymd.app import MDApp 19 | 20 | KV = """ 21 | MDScreen: 22 | md_bg_color: self.theme_cls.backgroundColor 23 | 24 | MDTextField: 25 | id: field 26 | pos_hint: {"center_x": .5, "center_y": .5} 27 | size_hint_x: .6 28 | 29 | MDTextFieldLeadingIcon: 30 | icon: "account" 31 | 32 | MDTextFieldHintText: 33 | text: "Hint text" 34 | 35 | MDTextFieldHelperText: 36 | text: "Helper text" 37 | mode: "persistent" 38 | 39 | MDTextFieldTrailingIcon: 40 | icon: "information" 41 | 42 | MDTextFieldMaxLengthText: 43 | max_text_length: 3 44 | """ 45 | 46 | 47 | class TestErrorState(MDApp): 48 | def build(self): 49 | return Builder.load_string(KV) 50 | 51 | def check_colors(self, *args): 52 | for group_name in [ 53 | "helper-text-color", 54 | "trailing-icons-color", 55 | "max-length-color", 56 | ]: 57 | group = self.root.ids.field.canvas.before.get_group(group_name)[0] 58 | assert group.rgba == self.theme_cls.errorColor 59 | 60 | group = self.root.ids.field.canvas.before.get_group( 61 | "leading-icons-color" 62 | )[0] 63 | assert group.rgba == self.theme_cls.onSurfaceVariantColor 64 | group = self.root.ids.field.canvas.after.get_group("hint-text-color")[0] 65 | assert group.rgba == self.theme_cls.errorColor 66 | self.stop() 67 | 68 | def set_max_text_length(self, *args): 69 | field = self.root.ids.field 70 | field.focus = True 71 | field.text = "Text" 72 | Clock.schedule_once(self.check_colors, 2) 73 | 74 | def on_start(self): 75 | Clock.schedule_once(self.set_max_text_length, 2) 76 | 77 | 78 | TestErrorState().run() 79 | -------------------------------------------------------------------------------- /kivymd/tests/textfield/test_error_state_color_helper_text_mode_on_focus.py: -------------------------------------------------------------------------------- 1 | # Test task: 2 | # 3 | # - helper text is not displayed (text fields without focus); 4 | # - helper text is displayed (red color, text fields with focus); 5 | # - helper text is not displayed (text fields without focus); 6 | 7 | from kivy.clock import Clock 8 | from kivy.lang import Builder 9 | 10 | from kivymd.app import MDApp 11 | 12 | KV = """ 13 | MDScreen: 14 | md_bg_color: self.theme_cls.backgroundColor 15 | 16 | MDTextField: 17 | id: field 18 | pos_hint: {"center_x": .5, "center_y": .5} 19 | text: "Text" 20 | size_hint_x: .6 21 | 22 | MDTextFieldLeadingIcon: 23 | icon: "account" 24 | 25 | MDTextFieldHintText: 26 | text: "Hint text" 27 | 28 | MDTextFieldHelperText: 29 | text: "Helper text" 30 | mode: "on_focus" 31 | 32 | MDTextFieldTrailingIcon: 33 | icon: "information" 34 | 35 | MDTextFieldMaxLengthText: 36 | max_text_length: 3 37 | """ 38 | 39 | 40 | class TestErrorStateColorHelperTextModeOnFocus(MDApp): 41 | state = "unchecked" 42 | 43 | def build(self): 44 | return Builder.load_string(KV) 45 | 46 | def check_helper_text_focus(self, *args): 47 | field = self.root.ids.field 48 | focus = field.focus 49 | 50 | instruction = self.root.ids.field.canvas.before.get_group( 51 | "helper-text-color" 52 | )[0] 53 | assert instruction.rgba == ( 54 | [0.0, 0.0, 0.0, 0.0] if not focus else self.theme_cls.errorColor 55 | ) 56 | 57 | if self.state == "checked": 58 | self.stop() 59 | 60 | if field.focus: 61 | field.focus = False 62 | self.state = "checked" 63 | Clock.schedule_once(self.check_helper_text_focus, 2) 64 | return 65 | 66 | field.focus = True 67 | Clock.schedule_once(self.check_helper_text_focus, 2) 68 | 69 | def on_start(self): 70 | Clock.schedule_once(self.check_helper_text_focus, 2) 71 | 72 | 73 | TestErrorStateColorHelperTextModeOnFocus().run() 74 | -------------------------------------------------------------------------------- /kivymd/tests/textfield/test_helper_text_mode_on_focus.py: -------------------------------------------------------------------------------- 1 | # Test task: 2 | # 3 | # - helper text is not displayed (text fields without focus); 4 | # - helper text is displayed (text fields with focus); 5 | # - helper text is not displayed (text fields without focus); 6 | 7 | from kivy.clock import Clock 8 | from kivy.lang import Builder 9 | 10 | from kivymd.app import MDApp 11 | 12 | KV = """ 13 | MDScreen: 14 | md_bg_color: self.theme_cls.backgroundColor 15 | 16 | MDTextField: 17 | id: field 18 | pos_hint: {"center_x": .5, "center_y": .5} 19 | text: "Text" 20 | size_hint_x: .6 21 | 22 | MDTextFieldLeadingIcon: 23 | icon: "account" 24 | 25 | MDTextFieldHintText: 26 | text: "Hint text" 27 | 28 | MDTextFieldHelperText: 29 | text: "Helper text" 30 | mode: "on_focus" 31 | 32 | MDTextFieldTrailingIcon: 33 | icon: "information" 34 | 35 | MDTextFieldMaxLengthText: 36 | max_text_length: 10 37 | """ 38 | 39 | 40 | class TestHelperTextModeOnFocus(MDApp): 41 | state = "unchecked" 42 | 43 | def build(self): 44 | return Builder.load_string(KV) 45 | 46 | def check_helper_text_focus(self, *args): 47 | field = self.root.ids.field 48 | focus = field.focus 49 | 50 | instruction = self.root.ids.field.canvas.before.get_group( 51 | "helper-text-color" 52 | )[0] 53 | # assert instruction.rgba == ( 54 | # [0.0, 0.0, 0.0, 0.0] 55 | # if not focus 56 | # else self.theme_cls.onSurfaceVariantColor 57 | # ) 58 | 59 | if self.state == "checked": 60 | self.stop() 61 | 62 | if field.focus: 63 | field.focus = False 64 | self.state = "checked" 65 | Clock.schedule_once(self.check_helper_text_focus, 2) 66 | return 67 | 68 | field.focus = True 69 | Clock.schedule_once(self.check_helper_text_focus, 2) 70 | 71 | def on_start(self): 72 | Clock.schedule_once(self.check_helper_text_focus, 2) 73 | 74 | 75 | TestHelperTextModeOnFocus().run() 76 | -------------------------------------------------------------------------------- /kivymd/tests/textfield/test_helper_text_mode_persistent.py: -------------------------------------------------------------------------------- 1 | from kivy.clock import Clock 2 | from kivy.lang import Builder 3 | 4 | from kivymd.app import MDApp 5 | 6 | KV = """ 7 | MDScreen: 8 | md_bg_color: self.theme_cls.backgroundColor 9 | 10 | MDTextField: 11 | id: field 12 | pos_hint: {"center_x": .5, "center_y": .5} 13 | text: "Text" 14 | size_hint_x: .6 15 | 16 | MDTextFieldLeadingIcon: 17 | icon: "account" 18 | 19 | MDTextFieldHintText: 20 | text: "Hint text" 21 | 22 | MDTextFieldHelperText: 23 | text: "Helper text" 24 | mode: "on_focus" 25 | 26 | MDTextFieldTrailingIcon: 27 | icon: "information" 28 | 29 | MDTextFieldMaxLengthText: 30 | max_text_length: 10 31 | """ 32 | 33 | 34 | class TestHelperTextModePersistent(MDApp): 35 | def build(self): 36 | return Builder.load_string(KV) 37 | 38 | def check_helper_text_without_focus(self, *args): 39 | if self.root.ids.field.focus: 40 | self.stop() 41 | 42 | assert self.root.ids.field._helper_text_label.texture_size != [0, 0] 43 | 44 | self.root.ids.field.focus = True 45 | Clock.schedule_once(self.check_helper_text_without_focus, 5) 46 | 47 | def on_start(self): 48 | Clock.schedule_once(self.check_helper_text_without_focus, 2) 49 | 50 | 51 | TestHelperTextModePersistent().run() 52 | -------------------------------------------------------------------------------- /kivymd/toast/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Brian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /kivymd/toast/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ("toast",) 2 | 3 | from .androidtoast import toast 4 | -------------------------------------------------------------------------------- /kivymd/toast/androidtoast.py: -------------------------------------------------------------------------------- 1 | """ 2 | AndroidToast 3 | ============ 4 | 5 | .. rubric:: Native implementation of toast for Android devices. 6 | 7 | .. code-block:: python 8 | 9 | # Will be automatically used native implementation of the toast 10 | # if your application is running on an Android device. 11 | # On desktop use `MDSnackbar < https://kivymd.readthedocs.io/en/latest/components/snackbar/>`_ 12 | 13 | from kivy.lang import Builder 14 | 15 | from kivymd.toast import toast 16 | from kivymd.app import MDApp 17 | 18 | KV = ''' 19 | MDScreen: 20 | md_bg_color: self.theme_cls.backgroundColor 21 | 22 | MDButton: 23 | pos_hint:{"center_x": .5, "center_y": .5} 24 | on_press: app.show_toast() 25 | 26 | MDButtonText: 27 | text: "Make toast" 28 | ''' 29 | 30 | 31 | class Example(MDApp): 32 | def build(self): 33 | return Builder.load_string(KV) 34 | 35 | def show_toast(self): 36 | toast("Hello World", True, 80, 200, 0) 37 | 38 | 39 | Example().run() 40 | """ 41 | 42 | __all__ = ("toast",) 43 | 44 | from kivy import platform 45 | 46 | if platform != "android": 47 | raise TypeError( 48 | f"{platform.capitalize()} platform does not support Android Toast" 49 | ) 50 | 51 | from android import mActivity 52 | from android.runnable import run_on_ui_thread 53 | from jnius import autoclass 54 | 55 | Toast = autoclass("android.widget.Toast") 56 | String = autoclass("java.lang.String") 57 | 58 | 59 | @run_on_ui_thread 60 | def toast(text, length_long=False, gravity=0, y=0, x=0): 61 | """ 62 | Displays a toast. 63 | 64 | :param length_long: the amount of time (in seconds) that the toast is 65 | visible on the screen; 66 | :param text: text to be displayed in the toast; 67 | :param length_long: duration of the toast, if `True` the toast 68 | will last 2.3s but if it is `False` the toast will last 3.9s; 69 | :param gravity: refers to the toast position, if it is 80 the toast will 70 | be shown below, if it is 40 the toast will be displayed above; 71 | :param y: refers to the vertical position of the toast; 72 | :param x: refers to the horizontal position of the toast; 73 | 74 | Important: if only the text value is specified and the value of 75 | the `gravity`, `y`, `x` parameters is not specified, their values will 76 | be 0 which means that the toast will be shown in the center. 77 | """ 78 | 79 | duration = Toast.LENGTH_SHORT if length_long else Toast.LENGTH_LONG 80 | t = Toast.makeText(mActivity, String(text), duration) 81 | t.setGravity(gravity, x, y) 82 | t.show() 83 | -------------------------------------------------------------------------------- /kivymd/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/hotreload/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/hotreload/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/packaging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/packaging/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/packaging/pyinstaller/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | PyInstaller hooks 3 | ================= 4 | 5 | Add ``hookspath=[kivymd.hooks_path]`` to your .spec file. 6 | 7 | Example of .spec file 8 | ===================== 9 | 10 | .. code-block:: python 11 | 12 | # -*- mode: python ; coding: utf-8 -*- 13 | 14 | import sys 15 | import os 16 | 17 | from kivy_deps import sdl2, glew 18 | 19 | from kivymd import hooks_path as kivymd_hooks_path 20 | 21 | path = os.path.abspath(".") 22 | 23 | a = Analysis( 24 | ["main.py"], 25 | pathex=[path], 26 | hookspath=[kivymd_hooks_path], 27 | win_no_prefer_redirects=False, 28 | win_private_assemblies=False, 29 | cipher=None, 30 | noarchive=False, 31 | ) 32 | pyz = PYZ(a.pure, a.zipped_data, cipher=None) 33 | 34 | exe = EXE( 35 | pyz, 36 | a.scripts, 37 | a.binaries, 38 | a.zipfiles, 39 | a.datas, 40 | *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)], 41 | debug=False, 42 | strip=False, 43 | upx=True, 44 | name="app_name", 45 | console=True, 46 | ) 47 | """ 48 | 49 | __all__ = ("hooks_path", "get_hook_dirs", "get_pyinstaller_tests") 50 | 51 | import os 52 | from pathlib import Path 53 | 54 | import kivymd 55 | 56 | hooks_path = str(Path(__file__).absolute().parent) 57 | """Path to hook directory to use with PyInstaller. 58 | See :mod:`kivymd.tools.packaging.pyinstaller` for more information.""" 59 | 60 | 61 | def get_hook_dirs(): 62 | return [hooks_path] 63 | 64 | 65 | def get_pyinstaller_tests(): 66 | return [os.path.join(kivymd.path, "tests", "pyinstaller")] 67 | 68 | 69 | if __name__ == "__main__": 70 | print(hooks_path) 71 | print(get_hook_dirs()) 72 | print(get_pyinstaller_tests()) 73 | -------------------------------------------------------------------------------- /kivymd/tools/packaging/pyinstaller/hook-kivymd.py: -------------------------------------------------------------------------------- 1 | """ 2 | PyInstaller hook for KivyMD 3 | =========================== 4 | 5 | Adds fonts, images and KV files to package. 6 | 7 | All modules from uix directory are added by Kivy hook. 8 | """ 9 | 10 | import os 11 | from pathlib import Path 12 | 13 | import kivymd 14 | 15 | datas = [ 16 | # Add `.ttf` files from the `kivymd/fonts` directory. 17 | ( 18 | kivymd.fonts_path, 19 | str(Path("kivymd").joinpath(Path(kivymd.fonts_path).name)), 20 | ), 21 | # Add files from the `kivymd/images` directory. 22 | ( 23 | kivymd.images_path, 24 | str(Path("kivymd").joinpath(Path(kivymd.images_path).name)), 25 | ), 26 | ] 27 | 28 | # Add `.kv. files from the `kivymd/uix` directory. 29 | for path_to_kv_file in Path(kivymd.uix_path).glob("**/*.kv"): 30 | datas.append( 31 | ( 32 | str(Path(path_to_kv_file).parent.joinpath("*.kv")), 33 | str( 34 | Path("kivymd").joinpath( 35 | "uix", 36 | str(Path(path_to_kv_file).parent).split( 37 | str(Path("kivymd").joinpath("uix")) + os.sep 38 | )[1], 39 | ) 40 | ), 41 | ) 42 | ) 43 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/patterns/MVC/Model/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/Model/database_firebase.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import socket 4 | 5 | import requests 6 | from firebase import firebase 7 | 8 | 9 | def get_connect(func, host="8.8.8.8", port=53, timeout=3): 10 | """Checks for an active Internet connection.""" 11 | 12 | def wrapped(*args): 13 | try: 14 | socket.setdefaulttimeout(timeout) 15 | socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect( 16 | (host, port) 17 | ) 18 | return func(*args) 19 | except Exception: 20 | return False 21 | 22 | return wrapped 23 | 24 | 25 | class DataBase: 26 | """ 27 | Your methods for working with the database should be implemented in this 28 | class. 29 | """ 30 | 31 | name = "Firebase" 32 | 33 | def __init__(self): 34 | self.DATABASE_URL = "https://fir-db73a-default-rtdb.firebaseio.com/" 35 | # Address for users collections. 36 | self.USER_DATA = "Userdata" 37 | # RealTime Database attribute. 38 | self.real_time_firebase = firebase.FirebaseApplication( 39 | self.DATABASE_URL, None 40 | ) 41 | 42 | @get_connect 43 | def get_data_from_collection(self, name_collection: str) -> dict | bool: 44 | """Returns data of the selected collection from the database.""" 45 | 46 | try: 47 | data = self.real_time_firebase.get( 48 | self.DATABASE_URL, name_collection 49 | ) 50 | except requests.exceptions.ConnectionError: 51 | return False 52 | 53 | return data 54 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/patterns/MVC/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/data/locales/po/en.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: 0.0.1\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2021-10-27 18:54+0300\n" 6 | "PO-Revision-Date: 2019-09-22 23:12+0300\n" 7 | "Last-Translator: KivyMD library https://github.com/kivymd/KivyMD\n" 8 | "Language-Team: KivyMD library https://github.com/kivymd/KivyMD\n" 9 | "Language: Russian\n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/data/locales/po/ru.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: 0.0.1\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2021-10-27 18:54+0300\n" 6 | "PO-Revision-Date: 2019-09-22 23:12+0300\n" 7 | "Last-Translator: KivyMD library https://github.com/kivymd/KivyMD\n" 8 | "Language-Team: KivyMD library https://github.com/kivymd/KivyMD\n" 9 | "Language: English\n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/libs/__init__.py: -------------------------------------------------------------------------------- 1 | # This package is for additional application modules. 2 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/libs/translation.py: -------------------------------------------------------------------------------- 1 | import gettext 2 | 3 | from kivy.lang import Observable 4 | 5 | 6 | class Translation(Observable): 7 | """Original source - https://github.com/tito/kivy-gettext-example.""" 8 | 9 | observers = [] 10 | 11 | def __init__(self, defaultlang, domian, resource_dir): 12 | super().__init__() 13 | self.ugettext = None 14 | self.lang = defaultlang 15 | self.domian = domian 16 | self.resource_dir = resource_dir 17 | self.switch_lang(self.lang) 18 | 19 | def _(self, text): 20 | return self.ugettext(text) 21 | 22 | def fbind(self, name, func, args, **kwargs): 23 | if name == "_": 24 | self.observers.append((func, args, kwargs)) 25 | else: 26 | return super().fbind(name, func, *args, **kwargs) 27 | 28 | def funbind(self, name, func, args, **kwargs): 29 | if name == "_": 30 | key = (func, args, kwargs) 31 | if key in self.observers: 32 | self.observers.remove(key) 33 | else: 34 | return super().funbind(name, func, *args, **kwargs) 35 | 36 | def switch_lang(self, lang): 37 | locales = gettext.translation( 38 | self.domian, self.resource_dir, languages=[lang] 39 | ) 40 | try: 41 | self.ugettext = locales.ugettext 42 | except AttributeError: 43 | self.ugettext = locales.gettext 44 | 45 | for func, largs, kwargs in self.observers: 46 | try: 47 | func(largs, None, None) 48 | except ReferenceError: 49 | pass 50 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/MVC/messages.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2021-10-27 18:54+0300\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | -------------------------------------------------------------------------------- /kivymd/tools/patterns/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/patterns/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/release/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/tools/release/__init__.py -------------------------------------------------------------------------------- /kivymd/tools/release/git_commands.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2022 Artem Bulgakov 2 | # 3 | # This file is distributed under the terms of the same license, 4 | # as the Kivy framework. 5 | 6 | import subprocess 7 | 8 | 9 | def command(cmd: list, capture_output: bool = False) -> str: 10 | """Run system command.""" 11 | 12 | print(f"Command: {subprocess.list2cmdline(cmd)}") 13 | if capture_output: 14 | out = subprocess.check_output(cmd) 15 | out = out.decode("utf-8") 16 | print(out.strip()) 17 | return out 18 | else: 19 | subprocess.check_call(cmd) 20 | return "" 21 | 22 | 23 | def get_previous_version() -> str: 24 | """Returns latest tag in git.""" 25 | 26 | command(["git", "checkout", "master"]) 27 | old_version = command( 28 | ["git", "describe", "--abbrev=0", "--tags"], capture_output=True 29 | ) 30 | old_version = old_version[:-1] # Remove \n 31 | return old_version 32 | 33 | 34 | def git_clean(ask: bool = True): 35 | """Clean git repository from untracked and changed files.""" 36 | 37 | # Check what files will be removed 38 | files_to_clean = command( 39 | ["git", "clean", "-dx", "--force", "--dry-run"], capture_output=True 40 | ).strip() 41 | # Ask before removing 42 | if ask and files_to_clean: 43 | while True: 44 | ans = input("Do you want to remove these files? (yes/no)").lower() 45 | if ans == "y" or ans == "yes": 46 | break 47 | elif ans == "n" or ans == "no": 48 | print("git clean is required. Exit") 49 | exit(0) 50 | 51 | # Remove all untracked files 52 | command(["git", "clean", "-dx", "--force"]) 53 | command(["git", "reset", "--hard"]) 54 | 55 | 56 | def git_commit(message: str, allow_error: bool = False, add_files: list = None): 57 | """Make commit.""" 58 | 59 | add_files = add_files if add_files else ["-A"] 60 | command(["git", "add", *add_files]) 61 | try: 62 | command(["git", "commit", "--all", "-m", message]) 63 | except subprocess.CalledProcessError as e: 64 | if not allow_error: 65 | raise e 66 | 67 | 68 | def git_tag(name: str): 69 | """Create tag.""" 70 | 71 | command(["git", "tag", name]) 72 | 73 | 74 | def git_push(branches_to_push: list, ask: bool = True, push: bool = False): 75 | """Push all changes.""" 76 | 77 | if ask: 78 | push = input("Do you want to push changes? (y)") in ( 79 | "", 80 | "y", 81 | "yes", 82 | ) 83 | 84 | cmd = ["git", "push", "--tags", "origin", "master", *branches_to_push] 85 | if push: 86 | command(cmd) 87 | else: 88 | print( 89 | f"Changes are not pushed. Command for manual pushing: {subprocess.list2cmdline(cmd)}" 90 | ) 91 | 92 | 93 | if __name__ == "__main__": 94 | git_clean(ask=True) 95 | -------------------------------------------------------------------------------- /kivymd/uix/appbar/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .appbar import ( 3 | MDActionBottomAppBarButton, 4 | MDActionTopAppBarButton, 5 | MDBottomAppBar, 6 | MDFabBottomAppBarButton, 7 | MDTopAppBar, 8 | MDTopAppBarLeadingButtonContainer, 9 | MDTopAppBarTitle, 10 | MDTopAppBarTrailingButtonContainer, 11 | ) 12 | -------------------------------------------------------------------------------- /kivymd/uix/appbar/appbar.kv: -------------------------------------------------------------------------------- 1 | 2 | size_hint_x: None 3 | width: self.minimum_width 4 | padding: "8dp", 0, 0, 0 5 | 6 | 7 | 8 | size_hint_x: None 9 | width: self.minimum_width 10 | padding: 0, 0, "8dp", 0 11 | spacing: "4dp" 12 | 13 | 14 | 15 | pos_hint: {"center_y": .5} 16 | color: 17 | self.theme_cls.onSurfaceVariantColor \ 18 | if self.theme_icon_color == "Primary" else \ 19 | self.icon_color 20 | 21 | 22 | 23 | text_size: self.size 24 | halign: "left" 25 | valign: "center" 26 | padding: 0, "5dp", 0, 0 27 | font_style: 28 | { \ 29 | "small": "Title", \ 30 | "medium": "Headline", \ 31 | "large": "Headline", \ 32 | }[self._appbar.type if self._appbar else "small"] 33 | role: 34 | { \ 35 | "small": "large", \ 36 | "medium": "small", \ 37 | "large": "medium", \ 38 | }[self._appbar.type if self._appbar else "large"] 39 | adaptive_width: True 40 | size_hint_x: 1 41 | 42 | 43 | 44 | canvas: 45 | Color: 46 | group: "md-top-app-bar-color" 47 | rgba: 48 | self.theme_cls.surfaceColor \ 49 | if self.theme_bg_color == "Primary" else \ 50 | self.md_bg_color 51 | Rectangle: 52 | pos: self.pos 53 | size: self.size 54 | 55 | orientation: 56 | "vertical" \ 57 | if self.type in ("medium", "large") else \ 58 | "horizontal" 59 | size_hint_y: None 60 | height: 61 | { \ 62 | "small": "64dp", \ 63 | "medium": "112dp", \ 64 | "large": "152dp", \ 65 | }[self.type] 66 | 67 | BoxLayout: 68 | id: root_box 69 | 70 | BoxLayout: 71 | id: text_box 72 | padding: "16dp", 0, "16dp", 0 73 | 74 | BoxLayout: 75 | id: title_box 76 | padding: "16dp", 0, "16dp", 0 77 | size_hint: 78 | (0, 0) \ 79 | if self.parent.type == "small" else \ 80 | (1, 1) 81 | 82 | 83 | 84 | elevation_level: 0 85 | theme_shadow_color: "Custom" 86 | shadow_color: self.theme_cls.transparentColor 87 | 88 | 89 | 90 | size_hint_y: None 91 | height: "80dp" 92 | elevation_level: 93 | 2 \ 94 | if self.theme_elevation_level == "Primary" else \ 95 | self.elevation_level 96 | shadow_softness: 97 | 2 \ 98 | if self.theme_shadow_softness == "Primary" else \ 99 | self.shadow_softness 100 | md_bg_color: 101 | self.theme_cls.surfaceContainerColor \ 102 | if self.theme_bg_color == "Primary" else \ 103 | self.md_bg_color 104 | -------------------------------------------------------------------------------- /kivymd/uix/badge/__init__.py: -------------------------------------------------------------------------------- 1 | from .badge import MDBadge # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/badge/badge.kv: -------------------------------------------------------------------------------- 1 | 2 | font_style: "Label" 3 | role: "small" 4 | radius: [self.texture_size[1] / 2, ] 5 | pos_hint: {"center_x": 0.5, "center_y": 0.5} 6 | padding: "4dp", "2dp" 7 | halign: "center" 8 | valign: "center" 9 | adaptive_size: True 10 | md_bg_color: self.theme_cls.errorColor 11 | text_color: self.theme_cls.onErrorColor 12 | size_hint: None, None 13 | size: self.texture_size 14 | pos: 15 | ( \ 16 | self.parent.x + (self.parent.width / 2), \ 17 | self.parent.y + (self.parent.height / 2) \ 18 | ) \ 19 | if self.parent else (0, 0) 20 | -------------------------------------------------------------------------------- /kivymd/uix/badge/badge.py: -------------------------------------------------------------------------------- 1 | """ 2 | Components/Badge 3 | ================ 4 | 5 | .. versionadded:: 2.0.0 6 | 7 | 8 | .. seealso:: 9 | 10 | `Material Design 3 spec, Badge `_ 11 | 12 | .. rubric:: Badges show notifications, counts, or status information on 13 | navigation items and icons. 14 | 15 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/badges.png 16 | :align: center 17 | 18 | Example 19 | ------- 20 | 21 | .. tabs:: 22 | 23 | .. tab:: Imperative python style with KV 24 | 25 | .. code-block:: python 26 | 27 | from kivy.lang import Builder 28 | 29 | from kivymd.app import MDApp 30 | 31 | KV = ''' 32 | MDScreen: 33 | md_bg_color: self.theme_cls.backgroundColor 34 | 35 | MDIcon: 36 | icon: "gmail" 37 | pos_hint: {'center_x': .5, 'center_y': .5} 38 | 39 | MDBadge: 40 | text: "12" 41 | ''' 42 | 43 | 44 | class Example(MDApp): 45 | def build(self): 46 | return Builder.load_string(KV) 47 | 48 | 49 | Example().run() 50 | 51 | .. tab:: Declarative python style 52 | 53 | .. code-block:: python 54 | 55 | from kivymd.app import MDApp 56 | from kivymd.uix.badge import MDBadge 57 | from kivymd.uix.label import MDIcon 58 | from kivymd.uix.screen import MDScreen 59 | 60 | 61 | class Example(MDApp): 62 | def build(self): 63 | self.theme_cls.theme_style = "Dark" 64 | return ( 65 | MDScreen( 66 | MDIcon( 67 | MDBadge( 68 | text="12", 69 | ), 70 | icon="gmail", 71 | pos_hint={'center_x': 0.5, 'center_y': 0.5}, 72 | ), 73 | md_bg_color=self.theme_cls.backgroundColor, 74 | ) 75 | ) 76 | 77 | 78 | Example().run() 79 | 80 | .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/badges-example.png 81 | :align: center 82 | """ 83 | 84 | __all__ = ("MDBadge",) 85 | 86 | import os 87 | 88 | from kivy.lang import Builder 89 | 90 | from kivymd import uix_path 91 | from kivymd.uix.label import MDLabel 92 | 93 | with open( 94 | os.path.join(uix_path, "badge", "badge.kv"), encoding="utf-8" 95 | ) as kv_file: 96 | Builder.load_string(kv_file.read()) 97 | 98 | 99 | class MDBadge(MDLabel): 100 | """ 101 | Badge class. 102 | 103 | .. versionadded:: 2.0.0 104 | 105 | For more information see in the 106 | :class:`~kivymd.uix.label.label.MDLabel` class documentation. 107 | """ 108 | -------------------------------------------------------------------------------- /kivymd/uix/behaviors/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Behaviors 3 | ========= 4 | 5 | Modules and classes implementing various behaviors for buttons etc. 6 | """ 7 | 8 | from .backgroundcolor_behavior import BackgroundColorBehavior 9 | 10 | # flake8: NOQA 11 | from .declarative_behavior import DeclarativeBehavior 12 | from .elevation import CommonElevationBehavior 13 | from .magic_behavior import MagicBehavior 14 | from .motion_behavior import ( 15 | MotionDialogBehavior, 16 | MotionDropDownMenuBehavior, 17 | MotionShackBehavior, 18 | ) 19 | from .ripple_behavior import CircularRippleBehavior, RectangularRippleBehavior 20 | from .rotate_behavior import RotateBehavior 21 | from .scale_behavior import ScaleBehavior 22 | from .stencil_behavior import StencilBehavior 23 | from .touch_behavior import TouchBehavior 24 | 25 | from .hover_behavior import HoverBehavior # isort:skip 26 | -------------------------------------------------------------------------------- /kivymd/uix/bottomsheet/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .bottomsheet import ( 3 | MDBottomSheet, 4 | MDBottomSheetDragHandle, 5 | MDBottomSheetDragHandleButton, 6 | MDBottomSheetDragHandleTitle, 7 | ) 8 | -------------------------------------------------------------------------------- /kivymd/uix/bottomsheet/bottomsheet.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | 3 | 4 | 5 | orientation: "vertical" 6 | size_hint_y: None 7 | height: self.minimum_height 8 | padding: "16dp", "8dp", "16dp", "16dp" 9 | 10 | BottomSheetDragHandle: 11 | canvas: 12 | Color: 13 | rgba: 14 | app.theme_cls.disabled_hint_text_color \ 15 | if not root.drag_handle_color else \ 16 | root.drag_handle_color 17 | SmoothRoundedRectangle: 18 | pos: self.pos 19 | size: self.size 20 | radius: [dp(4), ] 21 | 22 | size_hint: None, None 23 | size: "32dp", "4dp" 24 | pos_hint: {"center_x": .5} 25 | 26 | BottomSheetDragHandleContainer: 27 | id: header_container 28 | size_hint_y: None 29 | height: self.minimum_height 30 | 31 | 32 | 33 | orientation: "vertical" 34 | radius: "16dp", "16dp", 0, 0 35 | padding: 0, "8dp", 0, 0 36 | -x: 0 37 | width: Window.width if Window.width <= dp(640) else dp(640) 38 | pos_hint: {"center_x": .5} 39 | y: self.height * (self.open_progress - 1) 40 | 41 | BoxLayout: 42 | id: drag_handle_container 43 | size_hint_y: None 44 | height: self.minimum_height 45 | -------------------------------------------------------------------------------- /kivymd/uix/button/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .button import ( 3 | BaseButton, 4 | BaseFabButton, 5 | MDButton, 6 | MDButtonIcon, 7 | MDButtonText, 8 | MDExtendedFabButton, 9 | MDExtendedFabButtonIcon, 10 | MDExtendedFabButtonText, 11 | MDFabButton, 12 | MDIconButton, 13 | ) 14 | -------------------------------------------------------------------------------- /kivymd/uix/card/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .card import MDCard, MDCardSwipe, MDCardSwipeFrontBox, MDCardSwipeLayerBox 3 | -------------------------------------------------------------------------------- /kivymd/uix/chip/__init__.py: -------------------------------------------------------------------------------- 1 | from .chip import ( # NOQA F401 2 | MDChip, 3 | MDChipLeadingAvatar, 4 | MDChipLeadingIcon, 5 | MDChipText, 6 | MDChipTrailingIcon, 7 | ) 8 | -------------------------------------------------------------------------------- /kivymd/uix/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Controllers 3 | =========== 4 | 5 | .. versionadded:: 1.0.0 6 | 7 | Modules and classes that implement useful methods for getting information 8 | about the state of the current application window. 9 | """ 10 | 11 | from .windowcontroller import WindowController 12 | -------------------------------------------------------------------------------- /kivymd/uix/controllers/windowcontroller.py: -------------------------------------------------------------------------------- 1 | """ 2 | Controllers/WindowController 3 | ============================ 4 | 5 | .. versionadded:: 1.0.0 6 | 7 | Modules and classes that implement useful methods for getting information 8 | about the state of the current application window. 9 | 10 | Controlling the resizing direction of the application window 11 | ------------------------------------------------------------ 12 | 13 | .. code-block:: python 14 | 15 | # When resizing the application window, the direction of change will be 16 | # printed - 'left' or 'right'. 17 | 18 | from kivymd.app import MDApp 19 | from kivymd.uix.controllers import WindowController 20 | from kivymd.uix.screen import MDScreen 21 | 22 | 23 | class MyScreen(MDScreen, WindowController): 24 | def on_width(self, *args): 25 | print(self.get_window_width_resizing_direction()) 26 | 27 | 28 | class Test(MDApp): 29 | def build(self): 30 | return MyScreen() 31 | 32 | 33 | Test().run() 34 | """ 35 | 36 | from kivy.core.window import Window 37 | from kivy.core.window.window_sdl2 import WindowSDL 38 | from kivy.metrics import dp 39 | 40 | 41 | class WindowController: 42 | def __init__(self): 43 | self.window_resizing_direction = "unknown" 44 | self.real_device_type = "unknown" 45 | self.__width = Window.width 46 | Window.bind(on_resize=self._on_resize) 47 | 48 | def on_size(self, instance, size: list) -> None: 49 | """Called when the application screen size changes.""" 50 | 51 | window_width = size[0] 52 | 53 | if window_width < dp(500): 54 | self.real_device_type = "mobile" 55 | elif window_width < dp(1100): 56 | self.real_device_type = "tablet" 57 | else: 58 | self.real_device_type = "desktop" 59 | 60 | def get_real_device_type(self) -> str: 61 | """Returns the device type - 'mobile', 'tablet' or 'desktop'.""" 62 | 63 | return self.real_device_type 64 | 65 | def get_window_width_resizing_direction(self) -> str: 66 | """Return window width resizing direction - 'left' or 'right'.""" 67 | 68 | return self.window_resizing_direction 69 | 70 | def _set_window_width_resizing_direction(self, width: int) -> None: 71 | if self.__width > width: 72 | self.window_resizing_direction = "left" 73 | elif self.__width < width: 74 | self.window_resizing_direction = "right" 75 | 76 | def _on_resize( 77 | self, window_sdl2: WindowSDL, width: int, height: int 78 | ) -> None: 79 | self._set_window_width_resizing_direction(width) 80 | self.__width = width 81 | -------------------------------------------------------------------------------- /kivymd/uix/dialog/__init__.py: -------------------------------------------------------------------------------- 1 | from .dialog import ( # NOQA F401 2 | MDDialog, 3 | MDDialogButtonContainer, 4 | MDDialogContentContainer, 5 | MDDialogHeadlineText, 6 | MDDialogIcon, 7 | MDDialogSupportingText, 8 | ) 9 | -------------------------------------------------------------------------------- /kivymd/uix/divider/__init__.py: -------------------------------------------------------------------------------- 1 | from .divider import MDDivider # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/divider/divider.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas: 3 | Color: 4 | rgba: 5 | app.theme_cls.outlineVariantColor \ 6 | if not self.color else \ 7 | ( \ 8 | self.color \ 9 | if self.theme_divider_color == "Custom" else \ 10 | app.theme_cls.outlineVariantColor \ 11 | ) 12 | Rectangle: 13 | size: self.size 14 | pos: self.pos 15 | -------------------------------------------------------------------------------- /kivymd/uix/dropdownitem/__init__.py: -------------------------------------------------------------------------------- 1 | from .dropdownitem import MDDropDownItem, MDDropDownItemText # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/dropdownitem/dropdownitem.kv: -------------------------------------------------------------------------------- 1 | 2 | size_hint: None, None 3 | size: 4 | (self._size[0] + dp(12), self._size[1] + dp(8)) \ 5 | if self._size else \ 6 | (0, 0) 7 | md_bg_color: "red" 8 | 9 | canvas: 10 | Color: 11 | group: "drop-down-item-color" 12 | Rectangle: 13 | group: "drop-down-item-text" 14 | texture: 15 | self._drop_down_text.texture \ 16 | if self._drop_down_text else \ 17 | None 18 | size: 19 | self._drop_down_text.texture_size \ 20 | if self._drop_down_text else \ 21 | (0, 0) 22 | pos: 23 | self.x, self.y + dp(8) 24 | Color: 25 | group: "drop-down-item-triangle-color" 26 | Triangle: 27 | points: 28 | [ \ 29 | self.right + dp(0), self.y + dp(12), \ 30 | self.right - dp(6), self.y + dp(12), \ 31 | self.right - dp(6), self.y + root.height - dp(3) \ 32 | ] 33 | 34 | MDDivider: 35 | size_hint_x: None 36 | width: root._size[0] + dp(14) if root._size else 0 37 | 38 | 39 | 40 | size_hint_x: None 41 | width: self.texture_size[0] 42 | adaptive_width: True 43 | role: "small" 44 | -------------------------------------------------------------------------------- /kivymd/uix/expansionpanel/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .expansionpanel import ( 3 | MDExpansionPanel, 4 | MDExpansionPanelContent, 5 | MDExpansionPanelHeader, 6 | ) 7 | -------------------------------------------------------------------------------- /kivymd/uix/expansionpanel/expansionpanel.kv: -------------------------------------------------------------------------------- 1 | 2 | orientation: "vertical" 3 | size_hint_y: None 4 | height: self.minimum_height 5 | 6 | 7 | 8 | size_hint_y: None 9 | height: self.minimum_height 10 | 11 | 12 | 13 | size_hint_y: None 14 | height: self.minimum_height 15 | opacity: 0 16 | -------------------------------------------------------------------------------- /kivymd/uix/filemanager/__init__.py: -------------------------------------------------------------------------------- 1 | from .filemanager import MDFileManager # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/fitimage/__init__.py: -------------------------------------------------------------------------------- 1 | from .fitimage import FitImage # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/imagelist/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .imagelist import ( 3 | MDSmartTile, 4 | MDSmartTileImage, 5 | MDSmartTileOverlayContainer, 6 | ) 7 | -------------------------------------------------------------------------------- /kivymd/uix/imagelist/imagelist.kv: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | size_hint_y: 6 | (1 if self._smart_tile.overlap else None) \ 7 | if self._smart_tile else None 8 | height: 9 | ( \ 10 | self._smart_tile.height \ 11 | if self._smart_tile.overlap else \ 12 | self._smart_tile.height - (self._overlay_container.height if self._overlay_container else 0) \ 13 | ) \ 14 | if self._smart_tile else 0 15 | pos: 16 | ( \ 17 | ( \ 18 | (0, 0) \ 19 | if self._smart_tile.overlap else \ 20 | (0, (self._overlay_container.height if self._overlay_container else 0)) \ 21 | ) \ 22 | if self._smart_tile.overlay_mode == "footer" else \ 23 | (0, 0) \ 24 | ) \ 25 | if self._smart_tile else (0, 0) 26 | on_release: self._smart_tile.dispatch("on_release") 27 | on_press: self._smart_tile.dispatch("on_press") 28 | 29 | 30 | 31 | pos: 32 | ( \ 33 | (0, 0) \ 34 | if self._smart_tile.overlay_mode == "footer" else \ 35 | (0, self._smart_tile.height - self.height) \ 36 | ) \ 37 | if self._smart_tile else (0, 0) 38 | -------------------------------------------------------------------------------- /kivymd/uix/label/__init__.py: -------------------------------------------------------------------------------- 1 | from .label import MDIcon, MDLabel # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/label/label.kv: -------------------------------------------------------------------------------- 1 | #:import md_icons kivymd.icon_definitions.md_icons 2 | 3 | 4 | 5 | text_size: 6 | (self.width if not self.adaptive_width else None) \ 7 | if not self.adaptive_size else None, \ 8 | None 9 | color: 10 | self.text_color \ 11 | if self.text_color else \ 12 | self.theme_cls.onSurfaceColor 13 | disabled_color: 14 | app.theme_cls.onSurfaceColor[:-1] + \ 15 | [self.label_opacity_value_disabled_text] 16 | font_size: 17 | self.theme_cls.font_styles[self.font_style][self.role]["font-size"] \ 18 | if self.theme_font_size == "Primary" else self.font_size 19 | line_height: 20 | self.theme_cls.font_styles[self.font_style][self.role]["line-height"] \ 21 | if self.theme_line_height == "Primary" else self.line_height 22 | font_name: 23 | self.theme_cls.font_styles[self.font_style][self.role]["font-name"] \ 24 | if self.theme_font_name == "Primary" else self.font_name 25 | 26 | 27 | 28 | canvas: 29 | Color: 30 | rgba: (1, 1, 1, 1) if self.source else (0, 0, 0, 0) 31 | Rectangle: 32 | group: "rectangle" 33 | source: self.source 34 | pos: 35 | self.pos \ 36 | if not self.source else \ 37 | (self.x - (dp(18) / 2), self.y + dp(3)) 38 | size: 39 | (0, 0) if not self.source else (dp(18), dp(18)) 40 | 41 | font_style: "Icon" 42 | adaptive_size: True 43 | text: 44 | ( \ 45 | u"{}".format(md_icons[self.icon]) \ 46 | if self.icon in md_icons else \ 47 | "blank" \ 48 | ) \ 49 | if self.font_name == "Icons" else self.icon 50 | color: 51 | self.icon_color \ 52 | if self.icon_color else \ 53 | self.theme_cls.onSurfaceVariantColor 54 | -------------------------------------------------------------------------------- /kivymd/uix/list/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .list import ( 3 | BaseListItem, 4 | BaseListItemIcon, 5 | BaseListItemText, 6 | MDList, 7 | MDListItem, 8 | MDListItemHeadlineText, 9 | MDListItemLeadingAvatar, 10 | MDListItemLeadingIcon, 11 | MDListItemSupportingText, 12 | MDListItemTertiaryText, 13 | MDListItemTrailingCheckbox, 14 | MDListItemTrailingIcon, 15 | MDListItemTrailingSupportingText, 16 | ) 17 | -------------------------------------------------------------------------------- /kivymd/uix/menu/__init__.py: -------------------------------------------------------------------------------- 1 | from .menu import MDDropdownMenu # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/navigationbar/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .navigationbar import ( 3 | MDNavigationBar, 4 | MDNavigationItem, 5 | MDNavigationItemIcon, 6 | MDNavigationItemLabel, 7 | ) 8 | -------------------------------------------------------------------------------- /kivymd/uix/navigationdrawer/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .navigationdrawer import ( 3 | MDNavigationDrawer, 4 | MDNavigationDrawerDivider, 5 | MDNavigationDrawerHeader, 6 | MDNavigationDrawerItem, 7 | MDNavigationDrawerItemLeadingIcon, 8 | MDNavigationDrawerItemText, 9 | MDNavigationDrawerItemTrailingText, 10 | MDNavigationDrawerLabel, 11 | MDNavigationDrawerMenu, 12 | MDNavigationLayout, 13 | ) 14 | -------------------------------------------------------------------------------- /kivymd/uix/navigationdrawer/navigationdrawer.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | 3 | 4 | 5 | type: "filled" 6 | size_hint_x: None 7 | width: Window.width - dp(56) if Window.width <= dp(360) else dp(320) 8 | theme_bg_color: "Custom" 9 | shadow_radius: self.radius 10 | md_bg_color: 11 | self.theme_cls.surfaceContainerLowColor \ 12 | if not self.background_color else \ 13 | self.background_color 14 | x: 15 | (self.width * (self.open_progress - 1)) \ 16 | if self.anchor == "left" \ 17 | else (Window.width - self.width * self.open_progress) 18 | 19 | 20 | 21 | adaptive_height: True 22 | padding: "20dp", "0dp", "16dp", "16dp" 23 | text_color: 24 | self.theme_cls.onSurfaceColor \ 25 | if self.theme_text_color == "Primary" else \ 26 | self.text_color 27 | 28 | 29 | 30 | radius: self.height / 2 31 | ripple_color: self.theme_cls.onSecondaryContainerColor[:-1] + [0.12] 32 | theme_bg_color: "Custom" 33 | md_bg_color: 34 | self.theme_cls.surfaceContainerLowColor \ 35 | if not self.inactive_indicator_color else \ 36 | self.inactive_indicator_color 37 | 38 | 39 | 40 | text_color: 41 | self.theme_cls.onSurfaceVariantColor \ 42 | if self.theme_text_color == "Primary" else \ 43 | self.text_color 44 | 45 | 46 | 47 | font_style: "Label" 48 | role: "large" 49 | text_color: 50 | self.theme_cls.onSurfaceVariantColor \ 51 | if self.theme_text_color == "Primary" else \ 52 | self.text_color 53 | 54 | 55 | 56 | icon_color: 57 | self.theme_cls.onSurfaceVariantColor \ 58 | if self.theme_icon_color == "Primary" else \ 59 | self.icon_color 60 | 61 | 62 | 63 | size_hint_y: None 64 | height: self.minimum_height 65 | 66 | 67 | 68 | padding: 0, "4dp", 0, "4dp" 69 | size_hint_y: None 70 | height: self.minimum_height 71 | 72 | MDDivider: 73 | 74 | 75 | 76 | 77 | GridLayout: 78 | id: menu 79 | cols: 1 80 | size_hint_y: None 81 | height: self.minimum_height 82 | spacing: root.spacing 83 | 84 | -------------------------------------------------------------------------------- /kivymd/uix/navigationrail/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .navigationrail import ( 3 | MDNavigationRail, 4 | MDNavigationRailFabButton, 5 | MDNavigationRailItem, 6 | MDNavigationRailItemIcon, 7 | MDNavigationRailItemLabel, 8 | MDNavigationRailMenuButton, 9 | ) 10 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/__init__.py: -------------------------------------------------------------------------------- 1 | from .datepicker import ( # NOQA F401 2 | MDDockedDatePicker, 3 | MDModalDatePicker, 4 | MDModalInputDatePicker, 5 | ) 6 | from .timepicker import ( # NOQA F401 7 | MDTimePickerDialHorizontal, 8 | MDTimePickerDialVertical, 9 | MDTimePickerInput, 10 | ) 11 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/datepicker/__init__.py: -------------------------------------------------------------------------------- 1 | from .datepicker import ( # NOQA F401 2 | MDDockedDatePicker, 3 | MDModalDatePicker, 4 | MDModalInputDatePicker, 5 | ) 6 | -------------------------------------------------------------------------------- /kivymd/uix/pickers/timepicker/__init__.py: -------------------------------------------------------------------------------- 1 | from .timepicker import ( # NOQA F401 2 | MDTimePickerDialHorizontal, 3 | MDTimePickerDialVertical, 4 | MDTimePickerInput, 5 | ) 6 | -------------------------------------------------------------------------------- /kivymd/uix/progressindicator/__init__.py: -------------------------------------------------------------------------------- 1 | from .progressindicator import ( # NOQA F401 2 | MDCircularProgressIndicator, 3 | MDLinearProgressIndicator, 4 | ) 5 | -------------------------------------------------------------------------------- /kivymd/uix/progressindicator/progressindicator.kv: -------------------------------------------------------------------------------- 1 | 2 | canvas.before: 3 | PushMatrix 4 | Rotate: 5 | angle: self._rotation_angle 6 | origin: self.center 7 | canvas: 8 | Color: 9 | rgba: self.color if self.color else self.theme_cls.primaryColor 10 | a: self._alpha 11 | SmoothLine: 12 | cap: 'square' 13 | width: root.line_width 14 | circle: 15 | self.center_x, self.center_y, self.width / 2, \ 16 | self._angle_start, self._angle_end 17 | canvas.after: 18 | PopMatrix 19 | 20 | 21 | 22 | canvas: 23 | Clear 24 | # Inactive track. 25 | Color: 26 | rgba: 27 | self.theme_cls.surfaceContainerHighestColor \ 28 | if not self.track_color else \ 29 | self.track_color 30 | SmoothRoundedRectangle: 31 | radius: root.radius 32 | size: 33 | (self.width, self.height) \ 34 | if self.orientation == "horizontal" else \ 35 | (self.width, self.height) 36 | pos: 37 | (self.x, self.center_y - self.height / 2) \ 38 | if self.orientation == "horizontal" else \ 39 | (self.center_x - self.width / 2, self.y) 40 | # Active track. 41 | Color: 42 | rgba: 43 | self.theme_cls.primaryColor \ 44 | if not self.indicator_color else \ 45 | self.indicator_color 46 | SmoothRoundedRectangle: 47 | radius: root.radius 48 | size: 49 | ( \ 50 | self.width * self.value_normalized, \ 51 | self.height if self.height else dp(4) \ 52 | ) \ 53 | if self.orientation == "horizontal" else \ 54 | (self.width, self.height * self.value_normalized) 55 | pos: 56 | ( \ 57 | self.width * (1 - self.value_normalized) + self.x \ 58 | if self.reversed else self.x + self._x, \ 59 | self.center_y - self.height / 2 \ 60 | ) \ 61 | if self.orientation == "horizontal" \ 62 | else (self.center_x - self.width / 2, self.height \ 63 | * (1 - self.value_normalized) + self.y if self.reversed \ 64 | else self.y) 65 | -------------------------------------------------------------------------------- /kivymd/uix/refreshlayout/__init__.py: -------------------------------------------------------------------------------- 1 | from .refreshlayout import MDScrollViewRefreshLayout # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/refreshlayout/refreshlayout.kv: -------------------------------------------------------------------------------- 1 | #:import Window kivy.core.window.Window 2 | 3 | 4 | 5 | 6 | AnchorLayout: 7 | id: body_spinner 8 | size_hint: None, None 9 | size: dp(46), dp(46) 10 | y: Window.height 11 | pos_hint: {'center_x': .5} 12 | anchor_x: 'center' 13 | anchor_y: 'center' 14 | 15 | canvas: 16 | Clear 17 | Color: 18 | rgba: root.circle_color 19 | Ellipse: 20 | pos: self.pos 21 | size: self.size 22 | 23 | MDCircularProgressIndicator: 24 | id: spinner 25 | size_hint: None, None 26 | size: dp(30), dp(30) 27 | color: root.spinner_color 28 | -------------------------------------------------------------------------------- /kivymd/uix/segmentedbutton/__init__.py: -------------------------------------------------------------------------------- 1 | from .segmentedbutton import ( # NOQA F401 2 | MDSegmentButtonIcon, 3 | MDSegmentButtonLabel, 4 | MDSegmentedButton, 5 | MDSegmentedButtonItem, 6 | ) 7 | -------------------------------------------------------------------------------- /kivymd/uix/selectioncontrol/__init__.py: -------------------------------------------------------------------------------- 1 | from .selectioncontrol import MDCheckbox, MDSwitch, Thumb # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/slider/__init__.py: -------------------------------------------------------------------------------- 1 | from .slider import MDSlider, MDSliderHandle, MDSliderValueLabel # NOQA F401 2 | -------------------------------------------------------------------------------- /kivymd/uix/sliverappbar/__init__.py: -------------------------------------------------------------------------------- 1 | from .sliverappbar import ( 2 | MDSliverAppbar, 3 | MDSliverAppbarContent, 4 | MDSliverAppbarHeader, 5 | ) 6 | -------------------------------------------------------------------------------- /kivymd/uix/sliverappbar/sliverappbar.kv: -------------------------------------------------------------------------------- 1 | #:import ScrollEffect kivy.effects.scroll.ScrollEffect 2 | 3 | 4 | 5 | 6 | FloatLayout: 7 | id: float_box 8 | 9 | BoxLayout: 10 | canvas.after: 11 | Color: 12 | rgba: 13 | root.background_color \ 14 | if root.background_color else \ 15 | root.theme_cls.primaryColor 16 | a: root._opacity 17 | Rectangle: 18 | pos: self.pos 19 | size: self.size 20 | 21 | id: header 22 | size_hint_y: None 23 | height: root.max_height + root.radius[0] 24 | pos: self.x, root.height - root.max_height - root.radius[0] 25 | 26 | ScrollView: 27 | id: scroll 28 | effect_cls: ScrollEffect 29 | on_vbar: root.on_vbar() 30 | on_scroll_start: 31 | if not root._scroll_was_moving: root._scroll_was_moving = True 32 | 33 | BoxLayout: 34 | id: scroll_box 35 | orientation: "vertical" 36 | size_hint_y: None 37 | height: self.minimum_height 38 | 39 | BoxLayout: 40 | size_hint_y: None 41 | height: root.max_height 42 | 43 | 44 | 45 | adaptive_height: True 46 | md_bg_color: 47 | self.theme_cls.surfaceColor \ 48 | if self.theme_bg_color == "Primary" else \ 49 | self.md_bg_color 50 | -------------------------------------------------------------------------------- /kivymd/uix/snackbar/__init__.py: -------------------------------------------------------------------------------- 1 | from .snackbar import ( # NOQA F401 2 | MDSnackbar, 3 | MDSnackbarActionButton, 4 | MDSnackbarActionButtonText, 5 | MDSnackbarButtonContainer, 6 | MDSnackbarCloseButton, 7 | MDSnackbarSupportingText, 8 | MDSnackbarText, 9 | ) 10 | -------------------------------------------------------------------------------- /kivymd/uix/snackbar/snackbar.kv: -------------------------------------------------------------------------------- 1 | 2 | padding: 0, 0, "8dp", 0 3 | theme_bg_color: "Custom" 4 | theme_elevation_level: "Custom" 5 | theme_elevation_level: "Custom" 6 | style: "elevated" 7 | shadow_radius: self.radius 8 | elevation_level: 3 9 | size_hint_y: None 10 | height: self.minimum_height 11 | orientation: "vertical" 12 | md_bg_color: 13 | self.theme_cls.inverseSurfaceColor \ 14 | if not self.background_color else \ 15 | self.background_color 16 | 17 | BoxLayout: 18 | id: label_container 19 | padding: "16dp", "15dp", 0, "15dp" 20 | orientation: "vertical" 21 | size_hint_y: None 22 | height: self.minimum_height 23 | pos_hint: {"top": 1} 24 | spacing: "4dp" 25 | 26 | BoxLayout: 27 | id: button_container 28 | size_hint_x: None 29 | width: self.minimum_width 30 | 31 | 32 | 33 | size_hint_y: None 34 | height: self.minimum_height 35 | 36 | 37 | 38 | adaptive_size: True 39 | font_style: "Label" 40 | role: "large" 41 | markup: True 42 | text_color: 43 | self.theme_cls.inversePrimaryColor \ 44 | if self.theme_text_color == "Primary" else \ 45 | self.text_color 46 | 47 | 48 | 49 | adaptive_height: True 50 | font_style: "Body" 51 | role: "medium" 52 | markup: True 53 | text_color: 54 | self.theme_cls.inverseOnSurfaceColor \ 55 | if self.theme_text_color == "Primary" else \ 56 | self.text_color 57 | 58 | 59 | style: "text" 60 | pos_hint: {"right": 1} 61 | 62 | 63 | 64 | text_color: 65 | self.theme_cls.inverseOnSurfaceColor \ 66 | if self.theme_icon_color == "Primary" else \ 67 | self.icon_color 68 | 69 | 70 | 71 | adaptive_size: True 72 | font_style: "Label" 73 | role: "large" 74 | markup: True 75 | color: 76 | self.theme_cls.inversePrimaryColor \ 77 | if self.theme_text_color == "Primary" else \ 78 | self.text_color 79 | -------------------------------------------------------------------------------- /kivymd/uix/swiper/__init__.py: -------------------------------------------------------------------------------- 1 | from .swiper import MDSwiper, MDSwiperItem 2 | -------------------------------------------------------------------------------- /kivymd/uix/swiper/swiper.kv: -------------------------------------------------------------------------------- 1 | 2 | do_scroll_y: False 3 | bar_width: 0 4 | 5 | MDBoxLayout: 6 | id: anchor_scroll 7 | adaptive_width: True 8 | padding: [root.items_spacing, 0 ] 9 | 10 | 11 | 12 | size_hint: None, None 13 | 14 | 15 | <_ItemsBox> 16 | size_hint_x: None 17 | anchor_x: "center" 18 | anchor_y: "center" 19 | -------------------------------------------------------------------------------- /kivymd/uix/tab/__init__.py: -------------------------------------------------------------------------------- 1 | from .tab import ( # NOQA F401 2 | MDTabsBadge, 3 | MDTabsCarousel, 4 | MDTabsItem, 5 | MDTabsItemIcon, 6 | MDTabsItemSecondary, 7 | MDTabsItemText, 8 | MDTabsPrimary, 9 | MDTabsSecondary, 10 | ) 11 | -------------------------------------------------------------------------------- /kivymd/uix/tab/tab.kv: -------------------------------------------------------------------------------- 1 | #:import DampedScrollEffect kivy.effects.dampedscroll.DampedScrollEffect 2 | 3 | 4 | 5 | orientation: "vertical" 6 | size_hint_y: None 7 | height: self.minimum_height 8 | 9 | MDTabsScrollView: 10 | id: tab_scroll 11 | do_scroll_x: False if container.width <= self.width else True 12 | 13 | canvas.before: 14 | Color: 15 | rgba: 16 | root.md_bg_color \ 17 | if root.md_bg_color and root.theme_bg_color == "Custom" else \ 18 | root.theme_cls.surfaceColor 19 | Rectangle: 20 | pos: self.pos 21 | size: self.size 22 | 23 | GridLayout: 24 | id: container 25 | rows: 1 26 | size_hint: None, None 27 | width: self.minimum_width 28 | height: root.height 29 | 30 | canvas.before: 31 | Color: 32 | rgba: root.theme_cls.primaryColor 33 | SmoothRoundedRectangle: 34 | group: "md-tabs-rounded-rectangle" 35 | pos: 36 | self.x, \ 37 | self.y \ 38 | if not root._tabs_carousel else \ 39 | root._tabs_carousel.height 40 | size: 0, root.indicator_height 41 | radius: root.indicator_radius 42 | 43 | 44 | 45 | pos_hint: {"center_x": .5} 46 | theme_icon_color: "Custom" 47 | icon_color: self.theme_cls.onSurfaceVariantColor 48 | 49 | 50 | 51 | adaptive_size: True 52 | pos_hint: {"center_x": .5, "center_y": .5} 53 | padding: "36dp", 0, "36dp", 0 54 | font_style: "Title" 55 | role: "small" 56 | theme_text_color: "Custom" 57 | text_color: self.theme_cls.onSurfaceVariantColor 58 | 59 | 60 | 61 | orientation: "vertical" 62 | size_hint: None, None 63 | height: self.minimum_height 64 | spacing: "4dp" 65 | padding: 0, "12dp", 0, "8dp" 66 | 67 | 68 | 69 | 70 | 71 | 72 | size_hint: None, None 73 | height: "48dp" 74 | anchor_x: "center" 75 | anchor_y: "center" 76 | 77 | MDTabsItemSecondaryContainer: 78 | id: box_container 79 | size_hint: None, None 80 | size: self.minimum_size 81 | spacing: "8dp" 82 | 83 | 84 | 85 | size_hint: 1, None 86 | do_scroll_y: False 87 | bar_width: 0 88 | effect_cls: DampedScrollEffect 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /kivymd/uix/textfield/__init__.py: -------------------------------------------------------------------------------- 1 | # NOQA F401 2 | from .textfield import ( 3 | MDTextField, 4 | MDTextFieldHelperText, 5 | MDTextFieldHintText, 6 | MDTextFieldLeadingIcon, 7 | MDTextFieldMaxLengthText, 8 | MDTextFieldTrailingIcon, 9 | ) 10 | -------------------------------------------------------------------------------- /kivymd/uix/tooltip/__init__.py: -------------------------------------------------------------------------------- 1 | from .tooltip import ( # NOQA F401 2 | MDTooltip, 3 | MDTooltipPlain, 4 | MDTooltipRich, 5 | MDTooltipRichActionButton, 6 | MDTooltipRichSubhead, 7 | MDTooltipRichSupportingText, 8 | ) 9 | -------------------------------------------------------------------------------- /kivymd/uix/tooltip/tooltip.kv: -------------------------------------------------------------------------------- 1 | 2 | adaptive_size: True 3 | opacity: 0 4 | font_style: "Body" 5 | role: "small" 6 | padding: "8dp", "4dp", "8dp", "4dp" 7 | radius: [dp(4), ] 8 | scale_value_x: 0 9 | scale_value_y: 0 10 | text_color: 11 | self.theme_cls.inverseOnSurfaceColor \ 12 | if self.theme_text_color == "Primary" else \ 13 | self.text_color 14 | 15 | canvas.before: 16 | Color: 17 | rgba: 18 | self.theme_cls.inverseSurfaceColor \ 19 | if self.theme_bg_color == "Primary" else \ 20 | self.md_bg_color 21 | RoundedRectangle: 22 | size: self.size 23 | pos: self.pos 24 | radius: self.radius 25 | 26 | 27 | 28 | orientation: "vertical" 29 | scale_value_x: 0 30 | scale_value_y: 0 31 | opacity: 0 32 | radius: [dp(12), ] 33 | size_hint: None, None 34 | size: self.minimum_size 35 | padding: "16dp", "12dp", "16dp", "8dp" 36 | spacing: "4dp" 37 | elevation_level: 2 38 | elevation: self.elevation_levels[self.elevation_level] 39 | md_bg_color: 40 | self.theme_cls.surfaceContainerColor \ 41 | if self.theme_bg_color == "Primary" else \ 42 | self.md_bg_color 43 | shadow_softness: 44 | 2 \ 45 | if self.theme_shadow_softness == "Primary" else \ 46 | self.shadow_softness 47 | shadow_offset: 48 | (0, -1) \ 49 | if self.theme_shadow_offset == "Primary" else \ 50 | self.shadow_offset 51 | shadow_radius: [value - 2 for value in self.radius] 52 | 53 | 54 | bold: True 55 | adaptive_size: True 56 | font_style: "Title" 57 | role: "small" 58 | text_color: 59 | self.theme_cls.onSurfaceVariantColor \ 60 | if self.theme_text_color == "Primary" else \ 61 | self.text_color 62 | 63 | 64 | 65 | style: "text" 66 | _text_left_pad: 0 67 | _text_right_pad: 0 68 | ripple_effect: False 69 | 70 | 71 | 72 | adaptive_size: True 73 | font_style: "Body" 74 | role: "medium" 75 | text_color: 76 | self.theme_cls.onSurfaceVariantColor \ 77 | if self.theme_text_color == "Primary" else \ 78 | self.text_color 79 | -------------------------------------------------------------------------------- /kivymd/uix/transition/__init__.py: -------------------------------------------------------------------------------- 1 | from .transition import ( # NOQA F401 2 | MDFadeSlideTransition, 3 | MDSharedAxisTransition, 4 | MDSlideTransition, 5 | MDSwapTransition, 6 | ) 7 | -------------------------------------------------------------------------------- /kivymd/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kivymd/KivyMD/d2210785031dec10d4266f5bb90792d731668482/kivymd/utils/__init__.py -------------------------------------------------------------------------------- /kivymd/utils/fpsmonitor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Monitor module 3 | ============== 4 | 5 | The Monitor module is a toolbar that shows the activity of your current 6 | application : 7 | 8 | * FPS 9 | 10 | """ 11 | 12 | from kivy.clock import Clock 13 | from kivy.lang import Builder 14 | from kivy.properties import NumericProperty, OptionProperty, StringProperty 15 | from kivy.uix.label import Label 16 | 17 | Builder.load_string( 18 | """ 19 | : 20 | size_hint_y: None 21 | height: self.texture_size[1] 22 | text: root._fsp_value 23 | pos_hint: {root.anchor: 1} 24 | color: app.theme_cls.surfaceColor 25 | 26 | canvas.before: 27 | Color: 28 | rgba: app.theme_cls.onBackgroundColor 29 | Rectangle: 30 | pos: self.pos 31 | size: self.size 32 | """ 33 | ) 34 | 35 | 36 | class FpsMonitor(Label): 37 | """ 38 | Fps monitor class. 39 | 40 | For more information, see in the 41 | :class:`~kivy.uix.label.Label` class documentation. 42 | """ 43 | 44 | updated_interval = NumericProperty(0.5) 45 | """ 46 | FPS refresh rate. 47 | 48 | :attr:`updated_interval` is an :class:`~kivy.properties.NumericProperty` 49 | and defaults to `0.5`. 50 | """ 51 | 52 | anchor = OptionProperty("top", options=["top", "bottom"]) 53 | """ 54 | Monitor position. 55 | Available option are: 'top', 'bottom'. 56 | 57 | :attr:`anchor` is an :class:`~kivy.properties.OptionProperty` 58 | and defaults to `'top'`. 59 | """ 60 | 61 | _fsp_value = StringProperty() 62 | 63 | def start(self) -> None: 64 | """Monitor starting.""" 65 | 66 | Clock.schedule_interval(self.update_fps, self.updated_interval) 67 | 68 | def update_fps(self, *args) -> None: 69 | self._fsp_value = "FPS: %f" % Clock.get_fps() 70 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # Format Python 2 | [tool.black] 3 | line-length = 80 4 | target-version = ['py37', 'py38', 'py39', 'py310'] 5 | include = '(\.pyi?$|\.spec$)' 6 | exclude = ''' 7 | ( 8 | /( 9 | \.eggs 10 | | \.git 11 | | \.hg 12 | | \.mypy_cache 13 | | \.tox 14 | | \.venv 15 | | _build 16 | | buck-out 17 | | build 18 | | dist 19 | )/ 20 | | buildozer\.spec 21 | ) 22 | ''' 23 | 24 | # Sort imports 25 | # Settings to fit Black formatting 26 | # Taken from https://github.com/timothycrosley/isort/issues/694 27 | [tool.isort] 28 | line_length = 80 29 | include_trailing_comma = true 30 | multi_line_output = 3 31 | use_parentheses = true 32 | force_grid_wrap = 0 33 | ensure_newline_before_comments = true 34 | known_third_party = ["setuptools", "kivy", "requests", "PIL", "android", "jnius", "watchdog", "sphinx", "docutils", "autoapi", "unidecode"] 35 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = kivymd 3 | description = Set of widgets for Kivy inspired by Google's Material Design 4 | author = Andres Rodriguez, fork author - Ivanov Yuri 5 | author_email = kivydevelopment@gmail.com 6 | long_description = file: README.md 7 | long_description_content_type = text/markdown; charset=UTF-8 8 | keywords = kivymd, kivy, material, ui 9 | license = MIT 10 | license_file = LICENSE 11 | url = https://github.com/kivymd/KivyMD 12 | project_urls = 13 | Documentation=https://kivymd.readthedocs.io 14 | Wiki=https://github.com/kivymd/KivyMD/wiki 15 | Tracker=https://github.com/kivymd/KivyMD/issues 16 | classifiers = Development Status :: 4 - Beta 17 | License :: OSI Approved :: MIT License 18 | Programming Language :: Python :: 3 :: Only 19 | Programming Language :: Python :: 3 20 | Programming Language :: Python :: 3.7 21 | Programming Language :: Python :: 3.8 22 | Programming Language :: Python :: 3.9 23 | Programming Language :: Python :: 3.10 24 | Operating System :: OS Independent 25 | Operating System :: Android 26 | Operating System :: POSIX :: Linux 27 | Operating System :: POSIX :: BSD :: FreeBSD 28 | Operating System :: Microsoft :: Windows 29 | Operating System :: iOS 30 | Operating System :: MacOS 31 | Operating System :: MacOS :: MacOS X 32 | Environment :: MacOS X 33 | Environment :: Win32 (MS Windows) 34 | Environment :: X11 Applications 35 | Intended Audience :: Developers 36 | Intended Audience :: End Users/Desktop 37 | Intended Audience :: Information Technology 38 | Intended Audience :: Science/Research 39 | Topic :: Software Development :: User Interfaces 40 | Topic :: Scientific/Engineering :: Human Machine Interfaces 41 | Topic :: Scientific/Engineering :: Visualization 42 | platforms = any 43 | 44 | [upload] 45 | repository = https://upload.pypi.org/legacy/ 46 | 47 | [flake8] 48 | ignore = E501,W503,E203,E731,F401,F824 49 | max-line-length = 80 50 | exclude = .git/,__pycache__,build/,.eggs/,.buildozer 51 | statistics = true 52 | count = true 53 | --------------------------------------------------------------------------------