├── .github ├── FUNDING.yml └── workflows │ └── python-publish.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── kvdeveloper ├── __init__.py ├── __main__.py ├── assets │ ├── css │ │ ├── github-markdown.css │ │ └── prism.css │ ├── image_library │ │ ├── basic │ │ │ ├── discord128.png │ │ │ ├── discord256.png │ │ │ ├── discord512.png │ │ │ ├── discord64.png │ │ │ ├── discord720.png │ │ │ ├── google128.png │ │ │ ├── google256.png │ │ │ ├── google512.png │ │ │ ├── google64.png │ │ │ ├── google720.png │ │ │ ├── login_avatar128.png │ │ │ ├── login_illustration400.png │ │ │ ├── signup_avatar128.png │ │ │ ├── signup_illustration.png │ │ │ ├── stack_overflow128.png │ │ │ ├── stack_overflow256.png │ │ │ ├── stack_overflow512.png │ │ │ ├── stack_overflow64.png │ │ │ └── stack_overflow720.png │ │ └── kvdeveloper │ │ │ ├── kvdeveloper_banner.png │ │ │ ├── kvdeveloper_banner_outfit.png │ │ │ ├── kvdeveloper_banner_outfit720.png │ │ │ ├── kvdeveloper_banner_outfit_clear.png │ │ │ ├── kvdeveloper_banner_outfit_clear512.png │ │ │ ├── kvdeveloper_banner_outfit_clear720.png │ │ │ ├── kvdeveloper_banner_zoom.png │ │ │ ├── kvdeveloper_logo128.png │ │ │ ├── kvdeveloper_logo256.png │ │ │ ├── kvdeveloper_logo32.png │ │ │ ├── kvdeveloper_logo512.png │ │ │ ├── kvdeveloper_logo64.png │ │ │ ├── kvdeveloper_logo720.png │ │ │ └── templates │ │ │ ├── nav_dock.png │ │ │ └── nav_toolbar.png │ └── js │ │ └── prism.js ├── build_config.py ├── build_files │ ├── colab │ │ └── buildozer_action.ipynb │ └── github │ │ ├── buildozer_android_action.yml │ │ └── buildozer_ios_action.yml ├── cli.py ├── components │ ├── ITDCard │ │ ├── __init__.py │ │ ├── itdcard.kv │ │ └── itdcard.py │ ├── LazyManager │ │ ├── __init__.py │ │ └── lazymanager.py │ ├── LoadingLayout │ │ ├── __init__.py │ │ ├── loadinglayout.kv │ │ └── loadinglayout.py │ ├── ResponsiveGrid │ │ ├── __init__.py │ │ ├── responsive_grid.kv │ │ └── responsive_grid.py │ ├── __init__.py │ ├── container │ │ ├── __init__.py │ │ ├── container.kv │ │ └── container.py │ └── factory_registers.py ├── config.py ├── info_reader.py ├── internals │ └── firebase │ │ └── __init__.py ├── layouts │ ├── Auth │ │ └── 1 │ │ │ └── auth_screen.kv │ ├── Home │ │ ├── 1 │ │ │ ├── extensions.py │ │ │ └── home_screen.kv │ │ ├── 2 │ │ │ ├── extensions.py │ │ │ └── home_screen.kv │ │ └── 3 │ │ │ └── home_screen.kv │ └── Settings │ │ └── 1 │ │ ├── extensions.py │ │ └── settings_screen.kv ├── libs │ ├── PdfViewer │ │ ├── Android │ │ │ └── __init__.py │ │ ├── __init__.py │ │ └── iOS │ │ │ └── __init__.py │ └── __init__.py ├── module.py ├── structures │ ├── MVC │ │ ├── Controller │ │ │ └── __init__.py │ │ ├── Model │ │ │ ├── __init__.py │ │ │ └── base_model.py │ │ └── Utility │ │ │ ├── __init__.py │ │ │ └── observer.py │ └── __init__.py ├── templates │ ├── __init__.py │ ├── blank │ │ ├── README.md │ │ ├── View │ │ │ ├── SampleScreen │ │ │ │ ├── __init__.py │ │ │ │ ├── sample_screen.kv │ │ │ │ └── sample_screen.py │ │ │ ├── base_screen.kv │ │ │ ├── base_screen.py │ │ │ └── screens.py │ │ ├── main.py │ │ ├── mainh.py │ │ └── requirements.txt │ ├── nav_dock │ │ ├── README.md │ │ ├── View │ │ │ ├── HomeScreen │ │ │ │ ├── __init__.py │ │ │ │ ├── extensions.py │ │ │ │ ├── home_screen.kv │ │ │ │ └── home_screen.py │ │ │ ├── base_screen.kv │ │ │ ├── base_screen.py │ │ │ ├── extensions.py │ │ │ └── screens.py │ │ ├── main.py │ │ ├── mainh.py │ │ └── requirements.txt │ ├── nav_toolbar │ │ ├── README.md │ │ ├── View │ │ │ ├── HomeScreen │ │ │ │ ├── __init__.py │ │ │ │ ├── home_screen.kv │ │ │ │ └── home_screen.py │ │ │ ├── LoginScreen │ │ │ │ ├── __init__.py │ │ │ │ ├── login_screen.kv │ │ │ │ └── login_screen.py │ │ │ ├── base_screen.kv │ │ │ ├── base_screen.py │ │ │ ├── extensions.py │ │ │ └── screens.py │ │ ├── main.py │ │ ├── mainh.py │ │ └── requirements.txt │ └── p4a │ │ ├── build.tmpl.gradle │ │ └── gradle.json ├── utils.py └── view_base │ ├── buildozer.spec │ ├── default_component.py │ ├── default_screen.kv │ ├── default_screen.py │ ├── main.py │ └── registers.py └── pyproject.toml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Novfensec] 4 | patreon: Novfensec 5 | open_collective: KvDeveloper 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # novfensec 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: ['https://paypal.me/KARTAVYASHUKLA'] 16 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.pyo 4 | dist 5 | venv 6 | *.egg-info -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | - Demonstrating empathy and kindness toward other people 14 | - Being respectful of differing opinions, viewpoints, and experiences 15 | - Giving and gracefully accepting constructive feedback 16 | - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | - Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | - The use of sexualized language or imagery, and sexual attention or advances of any kind 22 | - Trolling, insulting or derogatory comments, and personal or political attacks 23 | - Public or private harassment 24 | - Publishing others’ private information, such as a physical or email address, without their explicit permission 25 | - Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Enforcement Responsibilities 28 | 29 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [novfensec@protonmail.com]. All complaints will be reviewed and investigated promptly and fairly. 40 | 41 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 42 | 43 | ## Enforcement Guidelines 44 | 45 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 46 | 47 | ### 1. Correction 48 | 49 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 50 | 51 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 52 | 53 | ### 2. Warning 54 | 55 | **Community Impact**: A violation through a single incident or series of actions. 56 | 57 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 58 | 59 | ### 3. Temporary Ban 60 | 61 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 62 | 63 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 64 | 65 | ### 4. Permanent Ban 66 | 67 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 68 | 69 | **Consequence**: A permanent ban from any sort of public interaction within the community. 70 | 71 | ## Attribution 72 | 73 | This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 74 | 75 | For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 76 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to KvDeveloper 2 | 3 | Thank you for considering contributing to KvDeveloper! We welcome contributions from everyone. By following these guidelines, you help us maintain a high standard of quality and ensure that your contributions are easy to review and integrate. 4 | 5 | ## Table of Contents 6 | 7 | 1. [Code of Conduct](#code-of-conduct) 8 | 2. [How to Contribute](#how-to-contribute) 9 | 3. [Reporting Bugs](#reporting-bugs) 10 | 4. [Requesting Features](#requesting-features) 11 | 5. [Coding Standards](#coding-standards) 12 | 6. [Support](#support) 13 | 14 | ## Code of Conduct 15 | 16 | Please read and follow our [Code of Conduct](https://github.com/Novfensec/KvDeveloper/blob/main/CODE_OF_CONDUCT.md) to ensure a welcoming and inclusive environment for everyone. 17 | 18 | ## How to Contribute 19 | 20 | 1. **Fork the Repository**: 21 | - Click the "Fork" button at the top right of the KvDeveloper GitHub page. 22 | - Clone your forked repository to your local machine: 23 | 24 | ```sh 25 | git clone https://github.com//KvDeveloper.git 26 | cd KvDeveloper 27 | ``` 28 | 29 | 2. **Install in Editable Mode**: 30 | - Set up the project in editable mode using pip: 31 | 32 | ```sh 33 | pip install -e . 34 | ``` 35 | 36 | 3. **Create a Branch**: 37 | - Create a new branch for your changes: 38 | 39 | ```sh 40 | git checkout -b my-feature-branch 41 | ``` 42 | 43 | 4. **Make Your Changes**: 44 | - Make your changes, ensuring proper type hinting and formatting your code using the Black formatter. 45 | 46 | ```sh 47 | pip install black 48 | black . 49 | ``` 50 | 51 | 5. **Commit Your Changes**: 52 | - Commit your changes with a clear and descriptive commit message: 53 | 54 | ```sh 55 | git add . 56 | git commit -m "Add feature X to KvDeveloper" 57 | ``` 58 | 59 | 6. **Push Your Branch**: 60 | - Push your branch to your forked repository: 61 | 62 | ```sh 63 | git push origin my-feature-branch 64 | ``` 65 | 66 | 7. **Create a Pull Request**: 67 | - Go to the KvDeveloper GitHub page and click the "New Pull Request" button. 68 | - Provide a detailed description of your changes, including screenshots if applicable. 69 | 70 | ## Reporting Bugs 71 | 72 | Before reporting a bug, please: 73 | 74 | 1. **Search existing issues** on our GitHub Issues page to see if the bug has already been reported. 75 | 2. If the bug has not been reported, create a new issue using the **Bug Report** template. Provide a clear and descriptive title, a detailed description, and any relevant screenshots or code snippets. 76 | 77 | ## Requesting Features 78 | 79 | To request a new feature: 80 | 81 | 1. **Search existing feature requests** on our GitHub Issues page to see if the feature has already been requested. 82 | 2. If the feature has not been requested, create a new issue using the **Feature Request** template. Provide a clear and descriptive title, a detailed description, and any relevant code or images. 83 | 84 | ## Coding Standards 85 | 86 | - **Type Hinting**: Ensure all functions and methods have proper type hints. 87 | - **Formatting**: Use the Black formatter to format your code. You can install it using `pip install black` and format your code by running `black .` in the project directory. 88 | - **Commit Messages**: Write clear and descriptive commit messages. Follow the Conventional Commits specification. 89 | 90 | ## Support 91 | 92 | Join the KvDeveloper community to get support, share your projects, and collaborate with other developers. Here are some ways you can connect with us: 93 | 94 | - **Discord**: Join our [KvDeveloper Community Server](https://discord.com/invite/gpubX9H8p7). 95 | - **Stack Overflow**: Feel free to reach out on our [Stack Overflow](https://stackoverflow.com/users/16486510/novfensec). 96 | - **GitHub Discussions**: Participate in discussions and ask questions in the [GitHub Discussions](https://github.com/Novfensec/KvDeveloper/discussions) section. 97 | - **Youtube - Admin**: Follow [@NovfensecInc](https://youtube.com/@NovfensecInc) to learn by building futuristic projects. 98 | - **YouTube - KvDeveloper**: Follow us on YouTube [@KvDeveloper](https://youtube.com/@KvDeveloper) for updates and announcements. 99 | 100 | If you encounter any issues or have questions, feel free to reach out to the community or submit an issue on GitHub. 101 | 102 | Thank you for your contributions and support! 103 | 104 | Kartavya Shukla 105 | 106 | Maintainer - [KvDeveloper](https://github.com/Novfensec/KvDeveloper) 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-2025 Kartavya Shukla 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KvDeveloper 2 | 3 | ```csharp 4 | _ __ ____ _ 5 | | |/ /_ _| _ \ _____ _____| | ___ _ __ ___ _ __ 6 | | ' /\ \ / / | | |/ _ \ \ / / _ \ |/ _ \| '_ \ / _ \ '__| 7 | | . \ \ V /| |_| | __/\ V / __/ | (_) | |_) | __/ | 8 | |_|\_\ \_/ |____/ \___| \_/ \___|_|\___/| .__/ \___|_| 9 | |_| 10 | ``` 11 | 12 | [![PyPI version](https://img.shields.io/pypi/v/kvdeveloper.svg?color=blueviolet&logo=pypi&logoColor=white)](https://pypi.org/project/kvdeveloper) 13 | [![Supported Python versions](https://img.shields.io/pypi/pyversions/kvdeveloper.svg?color=yellow&logo=python&logoColor=ffd43b)](#Installation) 14 | ![Downloads](https://static.pepy.tech/badge/kvdeveloper) 15 | [![Code style: Black](https://img.shields.io/badge/code%20style-black-000000.svg?color=purple)](https://github.com/psf/black) 16 | 17 | [![GitHub stars](https://img.shields.io/github/stars/Novfensec/KvDeveloper)](https://github.com/Novfensec/KvDeveloper/stargazers) 18 | [![GitHub forks](https://img.shields.io/github/forks/Novfensec/KvDeveloper)](https://github.com/Novfensec/KvDeveloper/network) 19 | [![GitHub repo size](https://img.shields.io/github/repo-size/Novfensec/KvDeveloper?color=red&logo=github&logoColor=white)](https://github.com/Novfensec/KvDeveloper) 20 | [![GitHub issues](https://img.shields.io/github/issues/Novfensec/KvDeveloper?color=blueviolet&logo=github&logoColor=white)](https://github.com/Novfensec/KvDeveloper/issues) 21 | 22 | 23 | 24 |

KvDeveloper is a PyPI module designed to streamline the development of Kivy and KivyMD applications. Inspired by Expo CLI for React Native, it offers starter templates and essential functionalities to kickstart your projects with ease. With features like predefined templates, MVC architecture support, and customizable options, KvDeveloper simplifies creating robust and organized Kivy projects. It supports Python 3.1+, Kivy 2.2.0+, and KivyMD 1.1.1+, making it a versatile tool for developers looking to enhance their Kivy development workflow.

25 | 26 | ## Community and Support 27 | [![Discord](https://img.shields.io/discord/566880874789076992?style=for-the-badge&color=7289da&logo=discord&logoColor=7289da)](https://discord.com/invite/U9bfkD6A4c) 28 | [![Stack Overflow](https://img.shields.io/static/v1?label=stackoverflow&message=questions&style=for-the-badge&color=orange&logo=stackoverflow&logoColor=fe7a17)](https://stackoverflow.com/users/16486510/novfensec) 29 | [![Reddit](https://img.shields.io/static/v1?label=reddit&message=KvDeveloper&style=for-the-badge&color=orangered&logo=reddit&logoColor=orangered)](https://reddit.com/r/KvDeveloper) 30 | [![GitHub Discussions](https://img.shields.io/static/v1?label=GitHub%20Discussions&message=ask%20questions&style=for-the-badge&color=blueviolet&logo=github&logoColor=white)](https://github.com/Novfensec/KvDeveloper/discussions) 31 | 32 | [![YouTube Admin](https://img.shields.io/static/v1?label=Youtube%20Admin&message=Novfensec%20Inc&color=black&logo=youtube&logoColor=ff0000)](https://youtube.com/@NovfensecInc) 33 | [![YouTube KvDeveloper](https://img.shields.io/static/v1?label=Youtube&message=KvDeveloper&color=blue&logo=youtube&logoColor=ff0000)](https://youtube.com/@KvDeveloper) 34 | 35 | Join the KvDeveloper community to get support, share your projects, and collaborate with other developers. Here are some ways you can connect with us: 36 | 37 | - **Discord**: Join our [KvDeveloper Community Server](https://discord.com/invite/U9bfkD6A4c). 38 | - **Stack Overflow**: Feel free to reach out on our [Stack Overflow](https://stackoverflow.com/users/16486510/novfensec). 39 | - **Reddit - KvDeveloper**: Feel free to join our [Reddit Community](https://reddit.com/r/KvDeveloper). 40 | - **GitHub Discussions**: Participate in discussions and ask questions in the [GitHub Discussions](https://github.com/Novfensec/KvDeveloper/discussions) section. 41 | - **Youtube - Admin**: Follow [@NovfensecInc](https://youtube.com/@NovfensecInc) to learn by building futuristic projects. 42 | - **YouTube - KvDeveloper**: Follow us on YouTube [@KvDeveloper](https://youtube.com/@KvDeveloper) for updates and announcements. 43 | 44 | [Documentation](https://novfensec.github.io/KvDeveloper.docs): Read the documentation. 45 | 46 | If you encounter any issues or have questions, feel free to reach out to the community or submit an issue on GitHub. 47 | 48 | ## Features 49 | - **Starter Templates**: Quickly create new Kivy and KivyMD projects with predefined templates. 50 | - **Layouts**: Build standard screens rapidly with prebuilt designs. Add layouts to any screen with a single command, making the development process faster and more efficient. 51 | - **MVC Structure**: Includes templates with Model-View-Controller (MVC) architecture. `(recommended KivyMD==2.0.1.dev0)` 52 | - **Navigation and Toolbar**: Templates with built-in navigation and toolbar screens. 53 | - **Customizable**: Easily extend and customize the templates and layouts to fit your project needs. 54 | - **Build Workflows and Jupyter Notebooks**: Generates build workflows for github based conversions and jupyter notebooks for colab based converions. 55 | 56 | ## Installation 57 | - Install KvDeveloper using pip: 58 | 59 | ```bash 60 | pip install kvdeveloper 61 | ``` 62 | 63 | **OR** 64 | 65 | ```bash 66 | pip install kvdeveloper[all] 67 | ``` 68 | 69 | - Install development version using pip `(requires git installation)`: 70 | 71 | ```bash 72 | pip install git+https://github.com/Novfensec/KvDeveloper.git@main 73 | ``` 74 | 75 | **OR** 76 | 77 | ```bash 78 | pip install https://github.com/Novfensec/KvDeveloper/archive/main.zip 79 | ``` 80 | 81 | 82 | ## Requirements 83 | - **Python**>=3.9 84 | 85 | - kivy>=2.0.0 `(recommended kivy==2.3.0)` 86 | 87 | - kivymd>=2.0.0 `(recommended kivymd==2.0.1.dev0)` 88 | 89 | - pillow>=10.0.0 90 | 91 | - typer>=0.12.3 92 | 93 | - rich>=13.7.1 94 | 95 | **OPTIONAL** 96 | 97 | - markdown2>=2.5.0 98 | 99 | - pyqt5 100 | 101 | - pyqtwebengine 102 | 103 | ## Usage 104 | - Create a new KivyMD project with a blank template: 105 | ```bash 106 | kvdeveloper create TestProject --template blank 107 | ``` 108 | 109 | - Create a new KivyMD project with navigation and toolbar with MVC architecture.: 110 | ```bash 111 | kvdeveloper create TestProject --template nav_toolbar --structure MVC 112 | ``` 113 | 114 | - Add a screen with a specific layout (e.g., Auth type 1): 115 | ```bash 116 | kvdeveloper add-screen TestScreen --layout auth1 117 | ``` 118 | 119 | - Add a layout to an existing screen (e.g., Home type 1): 120 | ```bash 121 | kvdeveloper add-layout home1 --name_screen TestScreen Test1Screen 122 | ``` 123 | 124 | - Add bootstrap like customizable components to the project directly: 125 | ```bash 126 | kvdeveloper add-component Container ResponsiveGrid ITDCard 127 | ``` 128 | 129 | - Create a new customizable components: 130 | ```bash 131 | kvdeveloper create-component MyComponent 132 | ``` 133 | 134 | - Register all custom fonts and components to Kivy bases: 135 | ```bash 136 | kvdeveloper register 137 | ``` 138 | 139 | - Get info about the template used for the project: 140 | ```bash 141 | kvdeveloper show-readme TestProject 142 | ``` 143 | 144 | - Generate github buildozer workflows for android conversion: 145 | ```bash 146 | kvdeveloper config-build-setup android --external github 147 | ``` 148 | 149 | Sample Repository: [Sample-KivyMD-App](https://github.com/Novfensec/SAMPLE-KIVYMD-APP) 150 | 151 | - Generate jupyter notebook for colab based android conversion [`Contains commands to import your app folder from your personal drive!`]: 152 | ```bash 153 | kvdeveloper config-build-setup android --external colab 154 | ``` 155 | 156 | ## Templates 157 | - **Blank Template**: A minimal template with the basic structure. 158 | 159 | - **Navigation Toolbar Template**: A template with navigation and toolbar screens. 160 |

161 | 165 |

166 | 167 | - **Navigation Dock Template**: A template navigation and toolbar screens with BottomNavigation, HomeScreen, LoginScreen and SettingsScreen components. 168 |

169 | 173 |

174 | 175 | - **MVC Architecture**: A template add-on following the MVC architecture. 176 | 177 | ## Components 178 | Create customizable bootstrap like components directly in your project. 179 | 180 | - **Container**: A responsive container with pre-defined padding calculations. 181 | 182 | - **ResponsiveGrid**: A responsive grid with pre-defined column calculations. 183 | 184 | - **ITDCard (Image Title Description Card)**: A responsive boostrap like card with image aspect-ratio calculations. 185 | 186 | - **LazyManager**: A [`MDScreenManager`](https://kivymd.readthedocs.io/en/latest/components/screenmanager) class instance with lazy loading abilities. 187 | 188 | - **LoadingLayout**: A FloatLayout class instance with centralised [`MDCircularProgressIndicator`](https://kivymd.readthedocs.io/en/latest/components/progressindicator/) widget. 189 | 190 | ## Contributing 191 | 192 | We welcome contributions from the community! If you're interested in contributing to KvDeveloper or its documentation, please read our [Contributing Guidelines](https://github.com/Novfensec/KvDeveloper/blob/main/CONTRIBUTING.md). 193 | 194 | You can contribute by: 195 | 196 | - Reporting bugs or suggesting features in the [Issues](https://github.com/Novfensec/KvDeveloper/issues) section. 197 | - Submitting pull requests to improve the documentation or the KvDeveloper tool. 198 | - Helping with translations or writing new guides. 199 | 200 | For more detailed instructions, please visit our [Contributing](https://github.com/Novfensec/KvDeveloper/blob/main/CONTRIBUTING.md) page. 201 | 202 | ## Acknowledgements 203 | 204 | [Kivy](https://github.com/kivy) 205 | 206 | [KivyMD](https://github.com/kivymd) 207 | 208 | ## License 209 | 210 | KvDeveloper is released under the [MIT License](https://github.com/Novfensec/KvDeveloper/blob/main/LICENSE). You're free to use, modify, and distribute this software as long as you adhere to the terms of the license. 211 | 212 | ## Contact 213 | For any inquiries, please contact us at [novfensec@protonmail.com](mailto:novfensec@protonmail.com). 214 | -------------------------------------------------------------------------------- /kvdeveloper/__init__.py: -------------------------------------------------------------------------------- 1 | __app_name__ = "kvdeveloper" 2 | __version__ = "2025.0.1.dev" 3 | -------------------------------------------------------------------------------- /kvdeveloper/__main__.py: -------------------------------------------------------------------------------- 1 | from kvdeveloper import __app_name__ 2 | from kvdeveloper.cli import app, console 3 | 4 | app_logo = """ 5 | _ __ ____ _ 6 | | |/ /_ _| _ \ _____ _____| | ___ _ __ ___ _ __ 7 | | ' /\ \ / / | | |/ _ \ \ / / _ \ |/ _ \| '_ \ / _ \ '__| 8 | | . \ \ V /| |_| | __/\ V / __/ | (_) | |_) | __/ | 9 | |_|\_\ \_/ |____/ \___| \_/ \___|_|\___/| .__/ \___|_| 10 | |_| 11 | """ 12 | 13 | 14 | def main() -> None: 15 | console.print(f"[bright_white]{app_logo}[/bright_white]") 16 | app(prog_name=__app_name__) 17 | 18 | 19 | if __name__ == "__main__": 20 | main() 21 | -------------------------------------------------------------------------------- /kvdeveloper/assets/css/github-markdown.css: -------------------------------------------------------------------------------- 1 | /* 2 | "GitHub Flavor", a GitHub flavored CSS style sheet for Markdown documents. 3 | Based on Chris Patuzzo's github.css (https://gist.github.com/tuzz/3331384). 4 | 5 | 6 | author: Fabrizio Musacchio (https://www.fabriziomusacchio.com) 7 | date: 03.01.2020 8 | 9 | 10 | License: 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | */ 31 | 32 | /*Main body*/ 33 | body { 34 | font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 35 | font-size: 18px; 36 | line-height: 1.4em; 37 | background-color:white; 38 | padding-top: 12px; 39 | padding-bottom: 12px; 40 | padding-left: 20px; 41 | padding-right: 20px; 42 | word-spacing: 1px; 43 | } 44 | 45 | body>*:first-child { 46 | margin-top: 0 !important; 47 | } 48 | 49 | body>*:last-child { 50 | margin-bottom: 0 !important; 51 | } 52 | 53 | /* Smooth scrolling */ 54 | @media screen and (prefers-reduced-motion: no-preference) { 55 | html { 56 | scroll-behavior: smooth; 57 | } 58 | } 59 | 60 | /*Links*/ 61 | a { 62 | color: #2d74ba; 63 | text-decoration: none; 64 | } 65 | 66 | a:hover { 67 | text-decoration: underline; 68 | color: #5e99d5; 69 | } 70 | 71 | a.absent { 72 | color: #cc0000; 73 | } 74 | 75 | a.anchor { 76 | display: block; 77 | padding-left: 30px; 78 | margin-left: -30px; 79 | cursor: pointer; 80 | position: absolute; 81 | top: 0; 82 | left: 0; 83 | bottom: 0; 84 | } 85 | 86 | /*Headlines*/ 87 | h1, 88 | h2, 89 | h3, 90 | h4, 91 | h5, 92 | h6 { 93 | margin: 20px 0 10px; 94 | padding: 0; 95 | line-height: 1.4em; 96 | font-weight: bold; 97 | -webkit-font-smoothing: antialiased; 98 | cursor: text; 99 | position: relative; 100 | } 101 | 102 | h1 tt, 103 | h1 code { 104 | font-size: inherit; 105 | } 106 | 107 | h2 tt, 108 | h2 code { 109 | font-size: inherit; 110 | } 111 | 112 | h3 tt, 113 | h3 code { 114 | font-size: inherit; 115 | } 116 | 117 | h4 tt, 118 | h4 code { 119 | font-size: inherit; 120 | } 121 | 122 | h5 tt, 123 | h5 code { 124 | font-size: inherit; 125 | } 126 | 127 | h6 tt, 128 | h6 code { 129 | font-size: inherit; 130 | } 131 | 132 | h1 { 133 | font-size: 28px; 134 | border-bottom: 1px solid #cccccc; 135 | color: black; 136 | } 137 | 138 | h2 { 139 | font-size: 24px; 140 | border-bottom: 1px solid #cccccc; 141 | color: black; 142 | } 143 | 144 | h3 { 145 | font-size: 18px; 146 | } 147 | 148 | h4 { 149 | font-size: 16px; 150 | } 151 | 152 | h5 { 153 | font-size: 14px; 154 | } 155 | 156 | h6 { 157 | color: #777777; 158 | font-size: 14px; 159 | } 160 | 161 | p, 162 | blockquote, 163 | ul, 164 | ol, 165 | dl, 166 | li, 167 | table, 168 | pre { 169 | margin: 15px 0; 170 | } 171 | 172 | /*Margins and paddings of the first line content*/ 173 | body>h2:first-child { 174 | margin-top: 0; 175 | padding-top: 0; 176 | } 177 | 178 | body>h1:first-child { 179 | margin-top: 0; 180 | padding-top: 0; 181 | } 182 | 183 | body>h1:first-child+h2 { 184 | margin-top: 0; 185 | padding-top: 0; 186 | } 187 | 188 | body>h3:first-child, 189 | body>h4:first-child, 190 | body>h5:first-child, 191 | body>h6:first-child { 192 | margin-top: 0; 193 | padding-top: 0; 194 | } 195 | 196 | a:first-child h1, 197 | a:first-child h2, 198 | a:first-child h3, 199 | a:first-child h4, 200 | a:first-child h5, 201 | a:first-child h6 { 202 | margin-top: 0; 203 | padding-top: 0; 204 | } 205 | 206 | h1 p, 207 | h2 p, 208 | h3 p, 209 | h4 p, 210 | h5 p, 211 | h6 p { 212 | margin-top: 0; 213 | } 214 | 215 | /*Lists*/ 216 | ul li, 217 | ol li { 218 | margin-top: .15em; 219 | margin-bottom: .15em; 220 | } 221 | 222 | ul li li { 223 | margin-left: -15px; 224 | } 225 | 226 | ul ul, 227 | ul ol, 228 | ol ol, 229 | ol ul { 230 | margin-top: 0; 231 | margin-bottom: 0em; 232 | } 233 | 234 | /*Footnotes*/ 235 | a[href^="#fn:"]:after { 236 | content: ')'; 237 | font-size: 0.83em; 238 | vertical-align: super; 239 | line-height: 0; 240 | } 241 | 242 | /*Highlights the BG of current jumped-to footnote:*/ 243 | li:target { 244 | background-color: #e8fef6; 245 | } 246 | 247 | .footnotes>ol>li>p { 248 | display: inline; 249 | } 250 | 251 | .footnotes ol { 252 | list-style: none; 253 | counter-reset: footnotes; 254 | padding-left: 15px; 255 | } 256 | 257 | .footnotes ol li { 258 | counter-increment: footnotes; 259 | } 260 | 261 | .footnotes ol li:before { 262 | /* background-color: #e8fef6; */ 263 | font-weight: bold; 264 | /* counter-reset: footnotes; */ 265 | content: counters(footnotes, ".") ")"; 266 | } 267 | 268 | .footnotes li { 269 | padding-bottom: 0.45em; 270 | } 271 | 272 | .footnotes { 273 | /* only use is for border, background-color of block */ 274 | /* border: dashed 0px #f4f5f8; */ 275 | background-color: #f4f5f8; 276 | padding: 0em 0em 0.25em 0em; 277 | } 278 | 279 | /*.footnotes:before{ 280 | content: "Footnotes"; 281 | font-size: 1.2em; 282 | font-weight: bold; 283 | line-height: 2.5em; 284 | padding-left: 0.5em; 285 | }*/ 286 | 287 | /*TOC (for DEVONthink Markdown documents; for other editors, try e.g. ".toc")*/ 288 | .TOC { 289 | background: #f4f5f8 none repeat scroll 0 0; 290 | border-radius: 10px; 291 | display: table; 292 | font-size: 95%; 293 | margin-bottom: 1em; 294 | padding-left: 2px; 295 | padding-top: 2px; 296 | padding-bottom: 2px; 297 | padding-right: 2px; 298 | width: 100%; 299 | } 300 | 301 | /*uncomment if you'd like to automatically add a title to your TOC:*/ 302 | /*.TOC:before { 303 | content: "Table of Contents"; 304 | font-weight: bold; 305 | font-size: 1.1em; 306 | color: #3973ad; 307 | padding-left: 1em; 308 | margin-bottom: -1em; 309 | line-height: 3em; 310 | }*/ 311 | 312 | .TOC li, 313 | .TOC ul, 314 | .TOC ul li { 315 | list-style: decimal; 316 | } 317 | 318 | .top-link { 319 | transition: all .25s ease-in-out; 320 | position: fixed; 321 | bottom: 0; 322 | right: 0; 323 | display: inline-flex; 324 | color: #000000; 325 | 326 | cursor: pointer; 327 | align-items: center; 328 | justify-content: center; 329 | margin: 0 2em 2em 0; 330 | border-radius: 50%; 331 | padding: .25em; 332 | width: 1em; 333 | height: 1em; 334 | background-color: #F8F8F8; 335 | } 336 | 337 | /*Check-Boxes/To-Do-Boxes (doesn't work in DEVONthink Markdown documents)*/ 338 | /* ul li.checkbox { 339 | appearance: none; 340 | background-color: #fff; 341 | margin: 0; 342 | font: inherit; 343 | color: #b2b2b2; 344 | width: 1.15em; 345 | height: 1.15em; 346 | border: 0.15em solid #b2b2b2; 347 | border-radius: 0.15em; 348 | } */ 349 | 350 | /* input[type="checkbox"] { 351 | appearance: none; 352 | background-color: #fff; 353 | margin: 0; 354 | font: inherit; 355 | color: #b2b2b2; 356 | width: 1.15em; 357 | height: 1.15em; 358 | border: 0.15em solid #b2b2b2; 359 | border-radius: 0.15em; 360 | } 361 | 362 | input[type="checkbox"]::before { 363 | content: " "; 364 | width: 0.65em; 365 | height: 0.65em; 366 | clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); 367 | } 368 | 369 | input[type="checkbox"]:checked::after { 370 | content: "_"; 371 | color: #cdef9a; 372 | box-shadow: inset 4em 2em #cdef9a; 373 | } 374 | 375 | .task-list-item { 376 | list-style-type: none; 377 | margin-top: 0px; 378 | margin-bottom: 0px; 379 | } 380 | 381 | .task-list-item input { 382 | margin: 0 0.2em .25em -1.6em; 383 | vertical-align: middle; 384 | padding-bottom: 0px; 385 | opacity: 1; 386 | } */ 387 | 388 | /*Definition lists*/ 389 | dl { 390 | padding: 0; 391 | } 392 | 393 | dl dt { 394 | font-size: 14px; 395 | font-weight: bold; 396 | font-style: italic; 397 | padding: 0; 398 | margin: 15px 0 5px; 399 | } 400 | 401 | dl dt:first-child { 402 | padding: 0; 403 | } 404 | 405 | dl dt> :first-child { 406 | margin-top: 0; 407 | } 408 | 409 | dl dt> :last-child { 410 | margin-bottom: 0; 411 | } 412 | 413 | dl dd { 414 | margin: 0 0 15px; 415 | padding: 0 15px; 416 | } 417 | 418 | dl dd> :first-child { 419 | margin-top: 0; 420 | } 421 | 422 | dl dd> :last-child { 423 | margin-bottom: 0; 424 | } 425 | 426 | /*Quotes*/ 427 | blockquote { 428 | border-left: 4px solid #dddddd; 429 | padding: 0 15px; 430 | font-style: normal; 431 | color: #5598c1; 432 | } 433 | 434 | /* 777777 */ 435 | blockquote> :first-child { 436 | margin-top: 0; 437 | } 438 | 439 | blockquote> :last-child { 440 | margin-bottom: 0; 441 | } 442 | 443 | /*Tables*/ 444 | table { 445 | padding: 0; 446 | border-collapse: collapse; 447 | margin-left: auto; 448 | margin-right: auto; 449 | text-align: center; 450 | } 451 | 452 | table tr { 453 | /* border-top: 1px solid #cccccc; */ 454 | background-color: white; 455 | margin: 0; 456 | padding: 0; 457 | } 458 | 459 | table tr:nth-child(2n) { 460 | background-color: #f8f8f8; 461 | } 462 | 463 | table tr th { 464 | font-weight: bold; 465 | /* border: 0.5px solid #cccccc; */ 466 | /* border-left: 0.5px solid #cccccc; 467 | border-right: 0.5px solid #cccccc; */ 468 | background-color: #eefbff; 469 | font-size: 14px; 470 | margin: 0; 471 | padding: 0.4em 0.35em 0.4em 0.35em; 472 | } 473 | 474 | table tr td { 475 | /* border: 1px solid #cccccc; */ 476 | margin: 0; 477 | font-size: 14px; 478 | padding: 5px 5px; 479 | } 480 | 481 | table tr th :first-child, 482 | table tr td :first-child { 483 | margin-top: 0; 484 | } 485 | 486 | table tr th :last-child, 487 | table tr td :last-child { 488 | margin-bottom: 0; 489 | } 490 | 491 | /*Images*/ 492 | img { 493 | max-width: 100%; 494 | } 495 | 496 | /*Alternative: center Images*/ 497 | /* img { 498 | display: block; 499 | max-width: 100%; 500 | margin-left: auto; 501 | margin-right: auto; } 502 | */ 503 | 504 | /*Span*/ 505 | span.frame { 506 | display: block; 507 | overflow: hidden; 508 | } 509 | 510 | span.frame>span { 511 | border: 1px solid #dddddd; 512 | display: block; 513 | float: left; 514 | overflow: hidden; 515 | margin: 13px 0 0; 516 | padding: 7px; 517 | width: auto; 518 | } 519 | 520 | span.frame span img { 521 | display: block; 522 | float: left; 523 | } 524 | 525 | span.frame span span { 526 | clear: both; 527 | color: #333333; 528 | display: block; 529 | padding: 5px 0 0; 530 | } 531 | 532 | span.align-center { 533 | display: block; 534 | overflow: hidden; 535 | clear: both; 536 | } 537 | 538 | span.align-center>span { 539 | display: block; 540 | overflow: hidden; 541 | margin: 13px auto 0; 542 | text-align: center; 543 | } 544 | 545 | span.align-center span img { 546 | margin: 0 auto; 547 | text-align: center; 548 | } 549 | 550 | span.align-right { 551 | display: block; 552 | overflow: hidden; 553 | clear: both; 554 | } 555 | 556 | span.align-right>span { 557 | display: block; 558 | overflow: hidden; 559 | margin: 13px 0 0; 560 | text-align: right; 561 | } 562 | 563 | span.align-right span img { 564 | margin: 0; 565 | text-align: right; 566 | } 567 | 568 | span.float-left { 569 | display: block; 570 | margin-right: 13px; 571 | overflow: hidden; 572 | float: left; 573 | } 574 | 575 | span.float-left span { 576 | margin: 13px 0 0; 577 | } 578 | 579 | span.float-right { 580 | display: block; 581 | margin-left: 13px; 582 | overflow: hidden; 583 | float: right; 584 | } 585 | 586 | span.float-right>span { 587 | display: block; 588 | overflow: hidden; 589 | margin: 13px auto 0; 590 | text-align: right; 591 | } 592 | 593 | /*Code blocks*/ 594 | code, 595 | tt { 596 | margin: 0 2px; 597 | padding: 0 5px; 598 | white-space: nowrap; 599 | background-color: #f8f8f8; 600 | border-radius: 3px; 601 | } 602 | 603 | pre code { 604 | margin: 0; 605 | padding: 0; 606 | white-space: pre; 607 | border: none; 608 | background: transparent; 609 | } 610 | 611 | .highlight pre { 612 | background-color: #f8f8f8; 613 | border: 1px solid #cccccc; 614 | font-size: 13px; 615 | line-height: 19px; 616 | overflow: auto; 617 | padding: 6px 10px; 618 | border-radius: 3px; 619 | } 620 | 621 | /*Preformatted text*/ 622 | pre { 623 | background-color: #f8f8f8; 624 | border: 1px solid #cccccc; 625 | /*font-size: 13px;*/ 626 | font-size: 0.90em !important; 627 | line-height: 1.6em !important; 628 | overflow: auto; 629 | padding: 6px 10px; 630 | border-radius: 3px; 631 | } 632 | 633 | pre code, 634 | pre tt { 635 | background-color: transparent; 636 | border: none; 637 | } 638 | 639 | /*Superscript text*/ 640 | sup { 641 | font-size: 0.83em; 642 | vertical-align: super; 643 | line-height: 0; 644 | } 645 | 646 | /*Subscript text*/ 647 | sub { 648 | font-size: 0.83em; 649 | vertical-align: sub; 650 | line-height: 0; 651 | } 652 | 653 | /*Define keyboard input*/ 654 | kbd { 655 | display: inline-block; 656 | padding: 3px 5px; 657 | font-size: 11px; 658 | line-height: 10px; 659 | color: #555; 660 | vertical-align: middle; 661 | background-color: #fcfcfc; 662 | border-bottom-color: #bbb; 663 | border-radius: 3px; 664 | box-shadow: inset 0 -1px 0 #bbb 665 | } 666 | 667 | /*Color of highlighted text*/ 668 | mark { 669 | background-color: #fdfdcc; 670 | color: black; 671 | } 672 | 673 | /*Adjustments for printing*/ 674 | * { 675 | -webkit-print-color-adjust: exact; 676 | } 677 | 678 | @media screen and (min-width: 914px) { 679 | body { 680 | width: 854px; 681 | margin: 0 auto; 682 | } 683 | } 684 | 685 | @media print { 686 | 687 | table, 688 | pre { 689 | page-break-inside: avoid; 690 | } 691 | 692 | pre { 693 | word-wrap: break-word; 694 | } 695 | 696 | body { 697 | padding: 2cm; 698 | } 699 | } -------------------------------------------------------------------------------- /kvdeveloper/assets/css/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.23.0 2 | https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apex+apl+applescript+aql+arduino+arff+asciidoc+aspnet+asm6502+autohotkey+autoit+bash+basic+batch+bbcode+birb+bison+bnf+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cil+clojure+cmake+coffeescript+concurnas+csp+crystal+css-extras+cypher+d+dart+dataweave+dax+dhall+diff+django+dns-zone-file+docker+dot+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+firestore-security-rules+flow+fortran+ftl+gml+gcode+gdscript+gedcom+gherkin+git+glsl+go+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+http+hpkp+hsts+ichigojam+icon+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keyman+kotlin+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+lolcode+lua+makefile+markdown+markup-templating+matlab+mel+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plsql+powerquery+powershell+processing+prolog+promql+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+q+qml+qore+r+racket+jsx+tsx+reason+regex+renpy+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+stan+iecst+stylus+swift+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+turtle+twig+typescript+typoscript+unrealscript+v+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+wiki+xeora+xml-doc+xojo+xquery+yaml+yang+zig&plugins=toolbar+copy-to-clipboard */ 3 | /** 4 | * okaidia theme for JavaScript, CSS and HTML 5 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/ 6 | * @author ocodia 7 | */ 8 | 9 | code[class*="language-"], 10 | pre[class*="language-"] { 11 | color: #f8f8f2; 12 | background: none; 13 | text-shadow: 0 1px rgba(0, 0, 0, 0.3); 14 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 15 | font-size: 1em; 16 | text-align: left; 17 | white-space: pre; 18 | word-spacing: normal; 19 | word-break: normal; 20 | word-wrap: normal; 21 | line-height: 1.5; 22 | 23 | -moz-tab-size: 4; 24 | -o-tab-size: 4; 25 | tab-size: 4; 26 | 27 | -webkit-hyphens: none; 28 | -moz-hyphens: none; 29 | -ms-hyphens: none; 30 | hyphens: none; 31 | } 32 | 33 | /* Code blocks */ 34 | pre[class*="language-"] { 35 | padding: 1em; 36 | margin: .5em 0; 37 | overflow: auto; 38 | border-radius: 0.3em; 39 | } 40 | 41 | :not(pre) > code[class*="language-"], 42 | pre[class*="language-"] { 43 | background: #272822; 44 | } 45 | 46 | /* Inline code */ 47 | :not(pre) > code[class*="language-"] { 48 | padding: .1em; 49 | border-radius: .3em; 50 | white-space: normal; 51 | } 52 | 53 | .token.comment, 54 | .token.prolog, 55 | .token.doctype, 56 | .token.cdata { 57 | color: #8292a2; 58 | } 59 | 60 | .token.punctuation { 61 | color: #f8f8f2; 62 | } 63 | 64 | .token.namespace { 65 | opacity: .7; 66 | } 67 | 68 | .token.property, 69 | .token.tag, 70 | .token.constant, 71 | .token.symbol, 72 | .token.deleted { 73 | color: #f92672; 74 | } 75 | 76 | .token.boolean, 77 | .token.number { 78 | color: #ae81ff; 79 | } 80 | 81 | .token.selector, 82 | .token.attr-name, 83 | .token.string, 84 | .token.char, 85 | .token.builtin, 86 | .token.inserted { 87 | color: #a6e22e; 88 | } 89 | 90 | .token.operator, 91 | .token.entity, 92 | .token.url, 93 | .language-css .token.string, 94 | .style .token.string, 95 | .token.variable { 96 | color: #f8f8f2; 97 | } 98 | 99 | .token.atrule, 100 | .token.attr-value, 101 | .token.function, 102 | .token.class-name { 103 | color: #e6db74; 104 | } 105 | 106 | .token.keyword { 107 | color: #66d9ef; 108 | } 109 | 110 | .token.regex, 111 | .token.important { 112 | color: #fd971f; 113 | } 114 | 115 | .token.important, 116 | .token.bold { 117 | font-weight: bold; 118 | } 119 | .token.italic { 120 | font-style: italic; 121 | } 122 | 123 | .token.entity { 124 | cursor: help; 125 | } 126 | 127 | div.code-toolbar { 128 | position: relative; 129 | } 130 | 131 | div.code-toolbar > .toolbar { 132 | position: absolute; 133 | top: .3em; 134 | right: .2em; 135 | transition: opacity 0.3s ease-in-out; 136 | opacity: 0; 137 | } 138 | 139 | div.code-toolbar:hover > .toolbar { 140 | opacity: 1; 141 | } 142 | 143 | /* Separate line b/c rules are thrown out if selector is invalid. 144 | IE11 and old Edge versions don't support :focus-within. */ 145 | div.code-toolbar:focus-within > .toolbar { 146 | opacity: 1; 147 | } 148 | 149 | div.code-toolbar > .toolbar .toolbar-item { 150 | display: inline-block; 151 | } 152 | 153 | div.code-toolbar > .toolbar a { 154 | cursor: pointer; 155 | } 156 | 157 | div.code-toolbar > .toolbar button { 158 | background: none; 159 | border: 0; 160 | color: inherit; 161 | font: inherit; 162 | line-height: normal; 163 | overflow: visible; 164 | padding: 0; 165 | -webkit-user-select: none; /* for button */ 166 | -moz-user-select: none; 167 | -ms-user-select: none; 168 | } 169 | 170 | div.code-toolbar > .toolbar a, 171 | div.code-toolbar > .toolbar button, 172 | div.code-toolbar > .toolbar span { 173 | color: #bbb; 174 | font-size: .8em; 175 | padding: 0 .5em; 176 | background: #f5f2f0; 177 | background: rgba(224, 224, 224, 0.2); 178 | box-shadow: 0 2px 0 0 rgba(0,0,0,0.2); 179 | border-radius: .5em; 180 | } 181 | 182 | div.code-toolbar > .toolbar a:hover, 183 | div.code-toolbar > .toolbar a:focus, 184 | div.code-toolbar > .toolbar button:hover, 185 | div.code-toolbar > .toolbar button:focus, 186 | div.code-toolbar > .toolbar span:hover, 187 | div.code-toolbar > .toolbar span:focus { 188 | color: inherit; 189 | text-decoration: none; 190 | } 191 | 192 | -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/discord128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/discord128.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/discord256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/discord256.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/discord512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/discord512.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/discord64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/discord64.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/discord720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/discord720.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/google128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/google128.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/google256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/google256.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/google512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/google512.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/google64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/google64.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/google720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/google720.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/login_avatar128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/login_avatar128.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/login_illustration400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/login_illustration400.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/signup_avatar128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/signup_avatar128.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/signup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/signup_illustration.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/stack_overflow128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/stack_overflow128.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/stack_overflow256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/stack_overflow256.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/stack_overflow512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/stack_overflow512.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/stack_overflow64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/stack_overflow64.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/basic/stack_overflow720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/basic/stack_overflow720.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit720.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit_clear.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit_clear512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit_clear512.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit_clear720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_outfit_clear720.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_banner_zoom.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo128.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo256.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo32.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo512.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo64.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/kvdeveloper_logo720.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/templates/nav_dock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/templates/nav_dock.png -------------------------------------------------------------------------------- /kvdeveloper/assets/image_library/kvdeveloper/templates/nav_toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/assets/image_library/kvdeveloper/templates/nav_toolbar.png -------------------------------------------------------------------------------- /kvdeveloper/build_config.py: -------------------------------------------------------------------------------- 1 | import os, typer 2 | 3 | from kvdeveloper.config import BUILD_DIR 4 | from kvdeveloper.module import console 5 | 6 | 7 | def generate_build_files(platform: str, external: str) -> None: 8 | """ 9 | Create necessary build files. 10 | 11 | :param external: External Build Environment. 12 | """ 13 | external_path = os.path.join(BUILD_DIR, external) 14 | if not os.path.isdir(external_path): 15 | console.print(f"External environment '{external}' not found.") 16 | raise typer.Exit(code=1) 17 | 18 | if external == "colab": 19 | external_file_path = os.path.join(external_path, "buildozer_action.ipynb") 20 | destination_file_path = os.path.join( 21 | os.getcwd(), "colab", "buildozer_action.ipynb" 22 | ) 23 | os.makedirs("colab", exist_ok=True) 24 | with open(external_file_path, "r", encoding="utf-8") as target_file: 25 | content = target_file.read() 26 | 27 | with open(destination_file_path, "w", encoding="utf-8") as template_file: 28 | template_file.write(content) 29 | 30 | console.print(f"Created file {destination_file_path}.") 31 | 32 | elif external == "github": 33 | external_file_path = os.path.join( 34 | external_path, f"buildozer_{platform}_action.yml" 35 | ) 36 | os.makedirs(".github/workflows", exist_ok=True) 37 | destination_file_path = os.path.join( 38 | os.getcwd(), ".github", "workflows", f"buildozer_{platform}_action.yml" 39 | ) 40 | with open(external_file_path, "r", encoding="utf-8") as target_file: 41 | content = target_file.read() 42 | 43 | with open(destination_file_path, "w", encoding="utf-8") as template_file: 44 | template_file.write(content) 45 | 46 | console.print(f"Created file {destination_file_path}.") 47 | -------------------------------------------------------------------------------- /kvdeveloper/build_files/colab/buildozer_action.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from google.colab import drive\n", 10 | "drive.mount(\"/content/drive/\")\n", 11 | "# use this if you have your app folder uploaded on your google drive" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "!cp -r /content/drive/\"yourfolderpath\" .\n", 21 | "# replace \"yourfolderpath\" with Drive folder path you want to copy\n", 22 | "# example: !cp -r /content/drive/MyDrive/WeatherApp ." 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "!pip install buildozer" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "!pip install cython==0.29.33" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "!sudo apt-get install -y \\\n", 50 | " python3-pip \\\n", 51 | " build-essential \\\n", 52 | " git \\\n", 53 | " python3 \\\n", 54 | " python3-dev \\\n", 55 | " ffmpeg \\\n", 56 | " libsdl2-dev \\\n", 57 | " libsdl2-image-dev \\\n", 58 | " libsdl2-mixer-dev \\\n", 59 | " libsdl2-ttf-dev \\\n", 60 | " libportmidi-dev \\\n", 61 | " libswscale-dev \\\n", 62 | " libavformat-dev \\\n", 63 | " libavcodec-dev \\\n", 64 | " libunwind-dev \\\n", 65 | " zlib1g-dev" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "!sudo apt-get install -y \\\n", 75 | " libgstreamer1.0 \\\n", 76 | " gstreamer1.0-plugins-base \\\n", 77 | " gstreamer1.0-plugins-good" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "!sudo apt-get install build-essential libsqlite3-dev \\\n", 87 | "sqlite3 bzip2 libbz2-dev zlib1g-dev libssl-dev \\\n", 88 | "openssl libgdbm-dev libgdbm-compat-dev liblzma-dev \\\n", 89 | "libreadline-dev libncursesw5-dev libffi-dev uuid-dev" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "!sudo apt install -y git zip unzip openjdk-17-jdk \\\n", 99 | "autoconf libtool pkg-config zlib1g-dev \\\n", 100 | "libncurses5-dev libncursesw5-dev libtinfo6 \\\n", 101 | "cmake libffi-dev libssl-dev automake " 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "# Set env variables for P4A Android App Bundle (Signed) [Required by Google Play]\n", 111 | "# Save and enable secrets for this notebook in colab.\n", 112 | "'''from google.colab import userdata\n", 113 | "\n", 114 | "P4A_RELEASE_KEYSTORE = userdata.get(\"P4A_RELEASE_KEYSTORE\")\n", 115 | "P4A_RELEASE_KEYSTORE_PASSWD = userdata.get(\"P4A_RELEASE_KEYSTORE_PASSWD\")\n", 116 | "P4A_RELEASE_KEYALIAS = userdata.get(\"P4A_RELEASE_KEYALIAS\")\n", 117 | "P4A_RELEASE_KEYALIAS_PASSWD = userdata.get(\"P4A_RELEASE_KEYALIAS_PASSWD\")\n", 118 | "\n", 119 | "%env P4A_RELEASE_KEYSTORE = $P4A_RELEASE_KEYSTORE\n", 120 | "%env P4A_RELEASE_KEYSTORE_PASSWD = $P4A_RELEASE_KEYSTORE_PASSWD\n", 121 | "%env P4A_RELEASE_KEYALIAS = $P4A_RELEASE_KEYALIAS\n", 122 | "%env P4A_RELEASE_KEYALIAS_PASSWD = $P4A_RELEASE_KEYALIAS_PASSWD'''" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "!buildozer init \n", 132 | "# run this if you want to generate a buildozer.spec file" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "!yes | buildozer -v android debug\n", 142 | "# run this to build an apk file for debugging." 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "'''!yes | buildozer -v android release'''\n", 152 | "# run this to build an aab (Android App Bundle) [Required by google play]." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "!buildozer android clean" 162 | ] 163 | } 164 | ], 165 | "metadata": { 166 | "language_info": { 167 | "name": "python" 168 | }, 169 | "orig_nbformat": 4 170 | }, 171 | "nbformat": 4, 172 | "nbformat_minor": 2 173 | } 174 | -------------------------------------------------------------------------------- /kvdeveloper/build_files/github/buildozer_android_action.yml: -------------------------------------------------------------------------------- 1 | # Sample repository at https://github.com/Novfensec/SAMPLE-KIVYMD-APP 2 | # Tic Tac Toe Game at https://github.com/Novfensec/Tic-Tac-Toe-Android 3 | 4 | name: Android Build 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | # Used to cache dependencies with a timeout 22 | - name: Get Date 23 | id: get-date 24 | run: | 25 | echo "date=$(date -u '+%Y%m%d')" >> $GITHUB_ENV 26 | shell: bash 27 | 28 | - name: Cache Buildozer global directory 29 | uses: actions/cache@v4 30 | with: 31 | path: ~/.buildozer 32 | key: buildozer-global-${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ hashFiles('buildozer.spec') }} 33 | restore-keys: | 34 | buildozer-global-${{ runner.os }}- 35 | 36 | - name: Cache Buildozer directory in app 37 | uses: actions/cache@v4 38 | with: 39 | path: .buildozer 40 | key: ${{ runner.os }}-buildozer-${{ github.run_id }}-${{ hashFiles('buildozer.spec') }} 41 | restore-keys: | 42 | ${{ runner.os }}-buildozer- 43 | 44 | - name: Cache Android SDK 45 | uses: actions/cache@v4 46 | with: 47 | path: ~/.buildozer/android/platform/android-sdk 48 | key: ${{ runner.os }}-android-sdk-${{ steps.get-date.outputs.date }} 49 | restore-keys: | 50 | ${{ runner.os }}-android-sdk- 51 | 52 | - name: Cache Android NDK 53 | uses: actions/cache@v4 54 | with: 55 | path: ~/.buildozer/android/platform/android-ndk-r25b #relative path as android-ndk-{version} 56 | key: ${{ runner.os }}-android-ndk-${{ steps.get-date.outputs.date }} 57 | restore-keys: | 58 | ${{ runner.os }}-android-ndk- 59 | 60 | # Install dependencies 61 | - name: Install dependencies 62 | run: | 63 | sudo apt update 64 | sudo apt-get install -y \ 65 | build-essential \ 66 | git \ 67 | ffmpeg \ 68 | libsdl2-dev \ 69 | libsdl2-image-dev \ 70 | libsdl2-mixer-dev \ 71 | libsdl2-ttf-dev \ 72 | libportmidi-dev \ 73 | libswscale-dev \ 74 | libavformat-dev \ 75 | libavcodec-dev \ 76 | libunwind-dev \ 77 | zlib1g-dev 78 | sudo apt-get install -y \ 79 | libsqlite3-dev \ 80 | sqlite3 \ 81 | bzip2 \ 82 | libbz2-dev \ 83 | zlib1g-dev \ 84 | openssl \ 85 | libgdbm-dev \ 86 | libgdbm-compat-dev \ 87 | liblzma-dev \ 88 | libreadline-dev \ 89 | uuid-dev \ 90 | libgstreamer1.0 \ 91 | gstreamer1.0-plugins-base \ 92 | gstreamer1.0-plugins-good 93 | sudo apt-get install -y \ 94 | zip \ 95 | unzip \ 96 | autoconf \ 97 | libtool \ 98 | pkg-config \ 99 | libncurses5-dev \ 100 | libncursesw5-dev \ 101 | libtinfo6 \ 102 | cmake \ 103 | libffi-dev \ 104 | libssl-dev \ 105 | automake 106 | 107 | # Set up Java 17 required by Gradle 108 | - name: Setup Java 17 required by Gradle 109 | uses: actions/setup-java@v4 110 | with: 111 | distribution: 'temurin' # or 'zulu', 'liberica', etc. 112 | java-version: '17' 113 | 114 | # Set up Python 115 | - name: Set up Python 116 | uses: actions/setup-python@v5 117 | with: 118 | python-version: '3.11' #set your python version 119 | 120 | # Install pip dependencies 121 | - name: Install pip dependencies 122 | run: | 123 | pip install --upgrade pip 124 | pip install buildozer cython==0.29.33 125 | 126 | ## Set env variables for P4A Android App Bundle (Signed) [Required by Google Play] 127 | ## https://gist.github.com/Guhan-SenSam/fa4ed215ef3419e7b3154de5cb71f641 128 | # - name: Set environment variables 129 | # env: 130 | # P4A_RELEASE_KEYSTORE: ${{ secrets.P4A_RELEASE_KEYSTORE }} 131 | # P4A_RELEASE_KEYSTORE_PASSWD: ${{ secrets.P4A_RELEASE_KEYSTORE_PASSWD }} 132 | # P4A_RELEASE_KEYALIAS: ${{ secrets.P4A_RELEASE_KEYALIAS }} 133 | # P4A_RELEASE_KEYALIAS_PASSWD: ${{ secrets.P4A_RELEASE_KEYALIAS_PASSWD }} 134 | 135 | # run: | 136 | # echo "P4A_RELEASE_KEYSTORE=$P4A_RELEASE_KEYSTORE" >> $GITHUB_ENV 137 | # echo "P4A_RELEASE_KEYSTORE_PASSWD=$P4A_RELEASE_KEYSTORE_PASSWD" >> $GITHUB_ENV 138 | # echo "P4A_RELEASE_KEYALIAS=$P4A_RELEASE_KEYALIAS" >> $GITHUB_ENV 139 | # echo "P4A_RELEASE_KEYALIAS_PASSWD=$P4A_RELEASE_KEYALIAS_PASSWD" >> $GITHUB_ENV 140 | 141 | # Build with Buildozer 142 | - name: Build with Buildozer 143 | id: buildozer 144 | run: | 145 | yes | buildozer -v android debug 146 | 147 | # Run below command to generate an Android App bundle [Required by Google Play] 148 | # yes | buildozer -v android release 149 | 150 | # Upload artifacts 151 | - name: Upload APK artifact 152 | uses: actions/upload-artifact@v4 153 | with: 154 | name: package 155 | path: | 156 | bin/*.apk 157 | bin/*.aab 158 | retention-days: 3 159 | -------------------------------------------------------------------------------- /kvdeveloper/build_files/github/buildozer_ios_action.yml: -------------------------------------------------------------------------------- 1 | name: iOS Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: macos-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | 19 | # Set up Python 20 | - name: Set up Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.11' 24 | 25 | # Install dependencies 26 | - name: Install dependencies 27 | run: | 28 | brew update 29 | brew install python3 30 | brew install autoconf automake libtool pkg-config 31 | python -m pip install --upgrade pip 32 | pip install --upgrade buildozer cython==0.29.36 pbxproj cookiecutter sh Pillow==8.2.0 33 | 34 | # Install Xcode 35 | - name: Install Xcode 36 | run: | 37 | sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer 38 | sudo xcodebuild -license accept 39 | 40 | # Set up environment variables for iOS 41 | - name: Set environment variables for iOS 42 | run: | 43 | export IOSSDK_ROOT=$(xcrun --sdk iphoneos --show-sdk-path) 44 | export CFLAGS="-isysroot $IOSSDK_ROOT" 45 | export LDFLAGS="-isysroot $IOSSDK_ROOT" 46 | shell: bash 47 | 48 | # Build with Buildozer for iOS 49 | - name: Build with Buildozer for iOS 50 | run: | 51 | buildozer -v ios debug 52 | 53 | # Upload artifacts 54 | - name: Upload IPA artifact 55 | uses: actions/upload-artifact@v4 56 | with: 57 | name: iOSPackage 58 | path: | 59 | bin/*.ipa -------------------------------------------------------------------------------- /kvdeveloper/components/ITDCard/__init__.py: -------------------------------------------------------------------------------- 1 | from .itdcard import ITDCard 2 | -------------------------------------------------------------------------------- /kvdeveloper/components/ITDCard/itdcard.kv: -------------------------------------------------------------------------------- 1 | : 2 | orientation: 'vertical' 3 | adaptive_height: True 4 | padding: [15, 15, 15, 15] 5 | spacing: 25 6 | theme_bg_color: "Custom" 7 | md_bg_color: app.theme_cls.surfaceContainerLowestColor 8 | 9 | FitImage: 10 | id: image 11 | size_hint: 1, None 12 | radius: self.parent.radius 13 | height: root.image_height 14 | source: root.source 15 | 16 | MDDivider: 17 | 18 | MDLabel: 19 | text: root.title 20 | font_style: "Title" 21 | role: "large" 22 | bold: True 23 | valign: "center" 24 | adaptive_height: True 25 | 26 | MDLabel: 27 | text: root.desc 28 | font_style: "Body" 29 | role: "large" 30 | multiline: True 31 | color: app.theme_cls.onSurfaceVariantColor 32 | valign: "center" 33 | adaptive_height: True 34 | 35 | MDLabel: 36 | font_style: "Body" 37 | role: "large" 38 | text: f"continue reading..." 39 | color: "blue" 40 | adaptive_height: True 41 | halign: "right" 42 | -------------------------------------------------------------------------------- /kvdeveloper/components/ITDCard/itdcard.py: -------------------------------------------------------------------------------- 1 | from kivy.properties import StringProperty, NumericProperty, ListProperty 2 | from kivy.uix.widget import Widget 3 | from kivymd.uix.card import MDCard 4 | 5 | 6 | class ITDCard(MDCard): 7 | 8 | title = StringProperty() 9 | """ 10 | Title for the Card. 11 | """ 12 | 13 | desc = StringProperty() 14 | """ 15 | Description for the card. 16 | """ 17 | 18 | source = StringProperty() 19 | """ 20 | Image path. 21 | """ 22 | 23 | image_height = NumericProperty() 24 | """ 25 | Image Height. 26 | """ 27 | 28 | image_ratio = ListProperty([4, 3]) 29 | """ 30 | Image Aspect ratio. 31 | """ 32 | 33 | def __init__(self, *args, **kwargs): 34 | super(ITDCard, self).__init__(*args, **kwargs) 35 | self.bind(size=self.adjust_image_size) 36 | 37 | def adjust_image_size(self, *args) -> None: 38 | w_ratio, h_ratio = self.image_ratio 39 | self.image_height = h_ratio / w_ratio * self.width 40 | -------------------------------------------------------------------------------- /kvdeveloper/components/LazyManager/__init__.py: -------------------------------------------------------------------------------- 1 | from .lazymanager import LazyManager 2 | -------------------------------------------------------------------------------- /kvdeveloper/components/LazyManager/lazymanager.py: -------------------------------------------------------------------------------- 1 | import os, glob 2 | 3 | from kivy.properties import DictProperty, ListProperty, ObjectProperty 4 | from kivy.lang import Builder 5 | from kivy.clock import mainthread 6 | 7 | from kivymd.uix.screenmanager import MDScreenManager 8 | 9 | 10 | class LazyManager(MDScreenManager): 11 | """ 12 | The LazyManager class inherits from kivymd.uix.screenmanager.MDScreenManager 13 | and adds lazy loading abilities to it. 14 | """ 15 | 16 | # Dictionary to hold the raw views 17 | raw_views = DictProperty() 18 | 19 | # List to keep track of upstream views 20 | upstream_views = ListProperty() 21 | 22 | # Object to define the loading layout 23 | loading_layout = ObjectProperty() 24 | 25 | def __init__(self, *args, **kwargs): 26 | """ 27 | Initialize the LazyManager and load the basic kvlang files. 28 | """ 29 | super(LazyManager, self).__init__(*args, **kwargs) 30 | self._load_basic_kvlang_files() 31 | 32 | def switch(self, name_screen: str, preload: bool = False, *args) -> None: 33 | """ 34 | Switch to the specified screen. If the screen is not already loaded, it loads the 35 | screen dynamically and adds it to the screen manager. 36 | 37 | :param name_screen: The name of the screen to switch to. 38 | :param preload: If True, preloads the screen without switching immediately. 39 | """ 40 | 41 | @mainthread 42 | def switch(*args) -> None: 43 | """ 44 | Switch the current screen to the specified screen on the main thread. 45 | """ 46 | self.current = name_screen 47 | self.dismiss_loader() 48 | 49 | if not self.has_screen(name_screen): 50 | module = self.raw_views[name_screen]["module"] 51 | self._load_kvlang_file(module) 52 | 53 | # Create the screen view and add it to the screen manager 54 | view = self.raw_views[name_screen]["object"]() 55 | view.name = name_screen 56 | view.manager_screens = self 57 | 58 | self.add_widget(view) 59 | self.upstream_views.extend(name_screen) 60 | 61 | if not preload: 62 | # Show the loading layout while the screen is being loaded 63 | self.start_loader() 64 | switch() 65 | 66 | def start_loader(self, *args) -> None: 67 | """ 68 | Display the loading layout on the current screen. 69 | """ 70 | self.loader_screen = self.current_screen 71 | self.loader_screen.add_widget(self.loading_layout) 72 | self.loading_layout.display() 73 | 74 | def dismiss_loader(self, *args) -> None: 75 | """ 76 | Remove the loading layout from the current screen. 77 | """ 78 | self.loader_screen.remove_widget(self.loading_layout) 79 | self.loading_layout.dismiss() 80 | 81 | def _load_basic_kvlang_files(self, *args) -> None: 82 | """ 83 | Load the basic kvlang files required for global components and base screens. 84 | """ 85 | global_components_filepath = os.path.join("View", "components", "**", "*.kv") 86 | for filepath in glob.glob(global_components_filepath, recursive=True): 87 | Builder.load_file(filepath) 88 | 89 | base_screen_filepath = os.path.join("View", "*.kv") 90 | for filepath in glob.glob(base_screen_filepath, recursive=True): 91 | Builder.load_file(filepath) 92 | 93 | def _load_kvlang_file(self, module: str, *args) -> None: 94 | """ 95 | Load the kvlang files dynamically based on the module path. 96 | 97 | :param module: The module path to convert to a kvlang file path. 98 | """ 99 | dynamic_filepath = os.path.join(module.replace(".", os.sep), "**", "*.kv") 100 | for filepath in glob.glob(dynamic_filepath, recursive=True): 101 | Builder.load_file(filepath) 102 | -------------------------------------------------------------------------------- /kvdeveloper/components/LoadingLayout/__init__.py: -------------------------------------------------------------------------------- 1 | from .loadinglayout import LoadingLayout 2 | -------------------------------------------------------------------------------- /kvdeveloper/components/LoadingLayout/loadinglayout.kv: -------------------------------------------------------------------------------- 1 | : 2 | size_hint: 1, 1 3 | md_bg_color: [0.7, 0.7, 0.7, 0.25] 4 | 5 | MDCircularProgressIndicator: 6 | size_hint: None, None 7 | size: "45dp", "45dp" 8 | pos_hint: {'center_x': 0.5,'center_y': 0.5} 9 | -------------------------------------------------------------------------------- /kvdeveloper/components/LoadingLayout/loadinglayout.py: -------------------------------------------------------------------------------- 1 | from kivymd.uix.floatlayout import MDFloatLayout 2 | from kivy.clock import mainthread 3 | 4 | 5 | class LoadingLayout(MDFloatLayout): 6 | def __init__(self, *args, **kwargs): 7 | super(LoadingLayout, self).__init__(*args, **kwargs) 8 | 9 | @mainthread 10 | def display(self, timeout: int = None, *args) -> None: 11 | initial = 0 12 | if timeout := timeout: 13 | while initial < timeout: 14 | self.opacity = 1 15 | self.opacity = 0 16 | self.opacity = 1 17 | 18 | @mainthread 19 | def dismiss(self, *args) -> None: 20 | self.opacity = 0 21 | -------------------------------------------------------------------------------- /kvdeveloper/components/ResponsiveGrid/__init__.py: -------------------------------------------------------------------------------- 1 | from .responsive_grid import ResponsiveGrid 2 | -------------------------------------------------------------------------------- /kvdeveloper/components/ResponsiveGrid/responsive_grid.kv: -------------------------------------------------------------------------------- 1 | : 2 | max_cols: 4 3 | min_cols: 1 4 | scale_width: "250dp" 5 | padding: 10 6 | spacing: 10 7 | cols: 1 8 | adaptive_height: True 9 | -------------------------------------------------------------------------------- /kvdeveloper/components/ResponsiveGrid/responsive_grid.py: -------------------------------------------------------------------------------- 1 | from kivy.properties import NumericProperty 2 | from kivymd.uix.gridlayout import MDGridLayout 3 | 4 | 5 | class ResponsiveGrid(MDGridLayout): 6 | 7 | max_cols = NumericProperty(4) 8 | """ 9 | Maximum number of columns in the grid. 10 | """ 11 | 12 | min_cols = NumericProperty(1) 13 | """ 14 | Minimum number of columns in the grid. 15 | """ 16 | 17 | scale_width = NumericProperty(35) 18 | """ 19 | Width of a single element. 20 | """ 21 | 22 | def __init__(self, *args, **kwargs): 23 | super(ResponsiveGrid, self).__init__(*args, **kwargs) 24 | self.bind(size=self.adjust_cols) 25 | 26 | def adjust_cols(self, *args): 27 | width, height = self.size 28 | 29 | for x in range(self.min_cols, -~self.max_cols): 30 | if (self.scale_width * x) < width < (self.scale_width * (x + 1)): 31 | self.cols = x 32 | break 33 | -------------------------------------------------------------------------------- /kvdeveloper/components/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from kivy.logger import Logger 3 | from kivy.lang import Builder 4 | from kvdeveloper import __version__ 5 | from kvdeveloper.config import COMPONENTS_DIR, def_dir 6 | import kvdeveloper.components.factory_registers 7 | 8 | for root, _, files in os.walk(COMPONENTS_DIR): 9 | for file in files: 10 | file_path = os.path.join(root, file) 11 | if file.endswith(".kv"): 12 | with open(file_path, "r", encoding="utf-8") as kv_file: 13 | content = kv_file.read() 14 | if not file in [os.path.basename(kv_files) for kv_files in Builder.files]: 15 | Builder.load_string(content, filename=file) 16 | 17 | 18 | Logger.info(f"KvDeveloper: {__version__}") 19 | Logger.info(f"KvDeveloper: Installed at {def_dir}") 20 | -------------------------------------------------------------------------------- /kvdeveloper/components/container/__init__.py: -------------------------------------------------------------------------------- 1 | from .container import Container 2 | -------------------------------------------------------------------------------- /kvdeveloper/components/container/container.kv: -------------------------------------------------------------------------------- 1 | : 2 | orientation: "vertical" 3 | type: "fluid" 4 | adaptive_height: True 5 | padding: [10, 10, 10, 10] 6 | spacing: 10 7 | state_hover: 0.4 8 | -------------------------------------------------------------------------------- /kvdeveloper/components/container/container.py: -------------------------------------------------------------------------------- 1 | from kivymd.uix.boxlayout import MDBoxLayout 2 | from kivymd.uix.behaviors.state_layer_behavior import StateLayerBehavior 3 | from kivy.properties import OptionProperty 4 | 5 | 6 | class Container(MDBoxLayout, StateLayerBehavior): 7 | type = OptionProperty( 8 | "fluid", 9 | options=("fluid", "small", "medium", "large"), 10 | ) 11 | """ 12 | The type of the conatiner. 13 | """ 14 | 15 | def __init__(self, *args, **kwargs): 16 | super(Container, self).__init__(*args, **kwargs) 17 | self.bind(size=self.adjust_size) 18 | 19 | def adjust_size(self, *args) -> None: 20 | if self.parent: 21 | if self.type == "small": 22 | self.padding = [self.width * 0.08, 10, self.width * 0.08, 10] 23 | elif self.type == "medium": 24 | self.padding = [self.width * 0.04, 10, self.width * 0.04, 10] 25 | elif self.type == "large": 26 | self.padding = [self.width * 0.01, 10, self.width * 0.01, 10] 27 | elif self.type == "fluid": 28 | pass 29 | -------------------------------------------------------------------------------- /kvdeveloper/components/factory_registers.py: -------------------------------------------------------------------------------- 1 | from kivy.factory import Factory 2 | 3 | # Alias for the register function from Factory 4 | register = Factory.register 5 | 6 | """ 7 | Registers custom components to the Kivy Factory. 8 | 9 | This code registers each component within the "components" directory to the Kivy Factory. 10 | Once registered, the components can be used without explicitly importing them elsewhere in the kvlang files. 11 | """ 12 | 13 | # Register the component with Kivy's Factory 14 | register("Container", module="kvdeveloper.components.Container") 15 | register("ITDCard", module="kvdeveloper.components.ITDCard") 16 | register("ResponsiveGrid", module="kvdeveloper.components.ResponsiveGrid") 17 | register("LazyManager", module="kvdeveloper.components.LazyManager") 18 | register("LoadingLayout", module="kvdeveloper.components.LoadingLayout") 19 | -------------------------------------------------------------------------------- /kvdeveloper/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import typer 3 | from pathlib import Path 4 | 5 | app = typer.Typer() 6 | def_dir = Path(__file__).parent 7 | 8 | # Configuration settings 9 | TEMPLATES = { 10 | "blank": "Basic structure with a SampleScreen component.", 11 | "nav_toolbar": "Navigation and toolbar screens with HomeScreen and LoginScreen components.", 12 | "nav_dock": "Navigation and toolbar screens with BottomNavigation, HomeScreen, LoginScreen and SettingsScreen components.", 13 | } 14 | TEMPLATES_DIR = os.path.join(def_dir, "templates") 15 | 16 | STRUCTURES = { 17 | "none": "No specific structure", 18 | "MVC": "Model-View-Controller architecture.", 19 | } 20 | STRUCTURES_DIR = os.path.join(def_dir, "structures") 21 | 22 | DEFAULT_TEMPLATE = "blank" 23 | DEFAULT_STRUCTURE = "none" 24 | 25 | VIEW_BASE = os.path.join(def_dir, "view_base") 26 | 27 | IMAGE_LIBRARY = os.path.join(def_dir, "assets", "image_library") 28 | 29 | BUILD_DIR = os.path.join(def_dir, "build_files") 30 | 31 | LAYOUTS_DIR = os.path.join(def_dir, "layouts") 32 | 33 | COMPONENTS = { 34 | "Container": "A responsive container with pre-defined padding calculations.", 35 | "ITCard": "(Image Title Description Card) A responsive boostrap like card with image aspect-ratio calculations.", 36 | "ResponsiveGrid": "A responsive grid with pre-defined column calculations.", 37 | "LazyManager": "A MDScreenManager class instance with lazy loading abilities.", 38 | "LoadingLayout": "A FloatLayout class instance with centralised MDCircularProgressIndicator widget.", 39 | } 40 | COMPONENTS_DIR = os.path.join(def_dir, "components") 41 | 42 | LIBS = { 43 | "PdfViewer": "PdfViewer module with appropriate pyjnius operations.", 44 | } 45 | 46 | LIBS_DIR = os.path.join(def_dir, "libs") 47 | 48 | # GRADLE 49 | GRADLE_TEMPLATE: str = os.path.join(def_dir, "templates/p4a/build.tmpl.gradle") 50 | 51 | p4a_version = "2024.01.21" 52 | 53 | P4A_URL = f"https://github.com/kivy/python-for-android/archive/refs/tags/v{p4a_version}.tar.gz" 54 | -------------------------------------------------------------------------------- /kvdeveloper/info_reader.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget 4 | from PyQt5.QtGui import QIcon 5 | from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings 6 | from markdown2 import markdown 7 | from kvdeveloper.config import def_dir, IMAGE_LIBRARY 8 | 9 | 10 | class MarkdownDisplayer(QMainWindow): 11 | def __init__(self) -> None: 12 | """ 13 | Initialize the MarkdownDisplayer window. 14 | """ 15 | super().__init__() 16 | self.init_ui() 17 | 18 | def init_ui(self) -> None: 19 | """ 20 | Set up the UI components for the main window. 21 | """ 22 | self.setWindowTitle("KvDeveloper") 23 | self.setWindowIcon(QIcon(f"{IMAGE_LIBRARY}/kvdeveloper/kvdeveloper_logo64.png")) 24 | self.setGeometry(100, 100, 1100, 600) 25 | 26 | # Create a QWebEngineView to render HTML content 27 | self.browser = QWebEngineView() 28 | 29 | # Enable JavaScript clipboard access in the browser 30 | settings = self.browser.settings() 31 | settings.setAttribute(QWebEngineSettings.JavascriptCanAccessClipboard, True) 32 | settings.setAttribute(QWebEngineSettings.JavascriptCanOpenWindows, True) 33 | 34 | # Set up the central widget and layout 35 | central_widget = QWidget(self) 36 | layout = QVBoxLayout(central_widget) 37 | layout.addWidget(self.browser) 38 | self.setCentralWidget(central_widget) 39 | 40 | def display_markdown(self, directory: str) -> None: 41 | """ 42 | Load and display the markdown content from a specified directory. 43 | 44 | :param directory: The directory containing the README.md file. 45 | """ 46 | # Read the markdown file 47 | with open( 48 | os.path.join(directory, "readme.md"), "r", encoding="utf-8" 49 | ) as md_file: 50 | markdown_content = md_file.read() 51 | 52 | # Convert markdown content to HTML 53 | html_content = markdown( 54 | markdown_content, 55 | extras=[ 56 | "fenced-code-blocks", 57 | "tables", 58 | "code-friendly", 59 | "strike", 60 | "markdown-in-html", 61 | ], 62 | ) 63 | 64 | # Load CSS and JavaScript for styling and syntax highlighting 65 | with open( 66 | os.path.join(def_dir, "assets", "css", "github-markdown.css"), 67 | "r", 68 | encoding="utf-8", 69 | ) as css_file: 70 | css_content = css_file.read() 71 | 72 | with open( 73 | os.path.join(def_dir, "assets", "js", "prism.js"), "r", encoding="utf-8" 74 | ) as js_file: 75 | js_content = js_file.read() 76 | 77 | with open( 78 | os.path.join(def_dir, "assets", "css", "prism.css"), "r", encoding="utf-8" 79 | ) as highlight_file: 80 | highlight_content = highlight_file.read() 81 | 82 | # Create the complete HTML content with embedded CSS and JavaScript 83 | base_html = f""" 84 | 85 | 86 | 87 | 88 | 89 | 93 | 94 | 95 | {html_content} 96 | 99 | 100 | 101 | """ 102 | 103 | # Display the HTML content in the QWebEngineView 104 | self.browser.setHtml(base_html) 105 | 106 | 107 | def info_reader(directory: str) -> None: 108 | """ 109 | Start a window displaying the markdown file. 110 | 111 | :param directory: The directory containing the README.md file. 112 | """ 113 | # Create the application and the main window 114 | app = QApplication(sys.argv) 115 | window = MarkdownDisplayer() 116 | 117 | # Show the window and display the markdown content 118 | window.display_markdown(directory) 119 | window.show() 120 | 121 | # Start the application event loop 122 | sys.exit(app.exec_()) 123 | -------------------------------------------------------------------------------- /kvdeveloper/internals/firebase/__init__.py: -------------------------------------------------------------------------------- 1 | import os, json, io, requests 2 | from pathlib import Path 3 | 4 | from kvdeveloper.module import console 5 | from kvdeveloper.utils import extract_tar_file 6 | 7 | 8 | def read_gradle_json(path: str): 9 | """ 10 | gradle.json from path to dict 11 | :param path: Path of the gradle.json 12 | :return: 13 | Python dict with the keywords ['classpath', 'plugin', 'bom', 'dep'] 14 | For each of the keys it does set(value) to avoid duplicates. 15 | """ 16 | gradle_json = {"classpath": [], "plugin": [], "bom": [], "dep": []} 17 | 18 | if os.path.exists(path): 19 | with open(path, "r", encoding="UTF-8") as file: 20 | g_json = json.load(file) 21 | for k, v in g_json.items(): 22 | if gradle_json.get(k) is not None: 23 | gradle_json[k].extend(v) 24 | gradle_json[k] = list(set(gradle_json[k])) 25 | 26 | else: 27 | raise ValueError(f"Key does not belong to {gradle_json.keys()}") 28 | return gradle_json 29 | 30 | 31 | def clone_p4a(p4a_dir: str, url: str): 32 | with requests.get(url, stream=True, timeout=3) as response: 33 | response.raise_for_status() 34 | with open(f"{p4a_dir}.tar.gz", "wb") as file: 35 | for chunk in response.iter_content(chunk_size=io.DEFAULT_BUFFER_SIZE): 36 | file.write(chunk) 37 | 38 | extract_tar_file(f"{p4a_dir}.tar.gz", Path(p4a_dir).parent) 39 | console.print(f"Removing: {p4a_dir}.tar.gz") 40 | os.remove(f"{p4a_dir}.tar.gz") 41 | 42 | with open("buildozer.spec", "r", encoding="utf-8") as build_file: 43 | content = build_file.readlines() 44 | 45 | with open("buildozer.spec", "w", encoding="utf-8") as target_build_file: 46 | for line in content: 47 | # Comment line with p4a.source_dir 48 | if line.lstrip().startswith("p4a.source_dir"): 49 | line = f"#{line}" 50 | 51 | target_build_file.write(line) 52 | 53 | line = f"p4a.source_dir = {p4a_dir}" 54 | console.print(f"Inserting at buildozer.spec: {line}") 55 | target_build_file.write(f"{line}\n") 56 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Auth/1/auth_screen.kv: -------------------------------------------------------------------------------- 1 | #: import images kvdeveloper.config.IMAGE_LIBRARY 2 | 3 | <{{parsed_name}}View>: 4 | MDStackLayout: 5 | size_hint: 1, 1 6 | spacing: 15 7 | 8 | MDBoxLayout: 9 | orientation: 'vertical' 10 | adaptive_height: True 11 | padding: [20, 20, 20, 20] 12 | 13 | MDIconButton: 14 | style: "standard" 15 | icon: "arrow-left" 16 | 17 | MDScrollView: 18 | MDGridLayout: 19 | cols: 1 20 | adaptive_height: True 21 | padding: [20, 20, 20, 20] 22 | spacing: 15 23 | 24 | LoginBoxLayout: 25 | orientation: 'vertical' 26 | adaptive_height: True 27 | spacing: "10dp" 28 | 29 | MDWidget: 30 | size_hint: 1, None 31 | height: dp(45) 32 | 33 | : 34 | MDBoxLayout: 35 | orientation: 'vertical' 36 | adaptive_height: True 37 | spacing: "10dp" 38 | 39 | MDLabel: 40 | text: "Login" 41 | font_style: "Display" 42 | role: "small" 43 | halign: "center" 44 | valign: "center" 45 | adaptive_height: True 46 | theme_text_color: "Custom" 47 | text_color: app.theme_cls.primaryColor 48 | 49 | MDAnchorLayout: 50 | size_hint: 1, None 51 | padding: [15, 15, 15, 15] 52 | FitImage: 53 | source: f"{images}/basic/login_illustration400.png" 54 | size_hint: 1, None 55 | adaptive_height: True 56 | 57 | MDWidget: 58 | size_hint: 1, None 59 | height: dp(8) 60 | 61 | MDBoxLayout: 62 | orientation: 'vertical' 63 | adaptive_height: True 64 | padding: [10, 10, 10, 10] 65 | spacing: "15dp" 66 | 67 | LoginForm: 68 | orientation: 'vertical' 69 | adaptive_height: True 70 | padding: [10, 10, 10, 10] 71 | spacing: "15dp" 72 | 73 | RelLayout: 74 | 75 | MDAnchorLayout: 76 | size_hint: 1, None 77 | height: content.height 78 | 79 | ExternalLoginSite: 80 | id: content 81 | orientation: 'vertical' 82 | adaptive_size: True 83 | padding: [10, 10, 10, 10] 84 | 85 | : 86 | adaptive_height: True 87 | 88 | MDDivider: 89 | size_hint: None, None 90 | 91 | MDLabel: 92 | text: "Or Login with" 93 | font_style: "Body" 94 | role: "medium" 95 | halign: "center" 96 | valign: "center" 97 | 98 | MDDivider: 99 | size_hint: None, None 100 | pos_hint: {'center_y': 0.5} 101 | 102 | : 103 | MDGridLayout: 104 | rows: 2 105 | cols: 2 106 | adaptive_size: True 107 | 108 | MDAnchorLayout: 109 | size_hint: None, None 110 | 111 | FitImage: 112 | source: f"{images}/basic/google128.png" 113 | size_hint: None, None 114 | size: "65sp","65sp" 115 | 116 | MDIconButton: 117 | icon: "blank" 118 | size_hint: 1, 1 119 | 120 | MDAnchorLayout: 121 | size_hint: None, None 122 | 123 | MDIconButton: 124 | icon: "github" 125 | size_hint: 1, 1 126 | theme_icon_color: "Custom" 127 | icon_color: "black" 128 | theme_font_size: "Custom" 129 | font_size: "75sp" 130 | 131 | MDAnchorLayout: 132 | size_hint: None, None 133 | 134 | FitImage: 135 | source: f"{images}/basic/stack_overflow128.png" 136 | size_hint: None, None 137 | size: "75sp","75sp" 138 | 139 | MDIconButton: 140 | icon: "blank" 141 | size_hint: 1, 1 142 | 143 | MDAnchorLayout: 144 | size_hint: None, None 145 | 146 | FitImage: 147 | source: f"{images}/kvdeveloper/kvdeveloper_logo128.png" 148 | size_hint: None, None 149 | size: "75sp","75sp" 150 | 151 | MDIconButton: 152 | icon: "blank" 153 | size_hint: 1, 1 154 | 155 | : 156 | MDTextField: 157 | mode: "filled" 158 | theme_line_color: "Custom" 159 | line_color_normal: [1, 1, 1, 0] 160 | 161 | MDTextFieldLeadingIcon: 162 | icon: "account" 163 | theme_icon_color: "Custom" 164 | icon_color_normal: app.theme_cls.primaryColor 165 | icon_color_focus: app.theme_cls.primaryColor 166 | 167 | MDTextFieldHintText: 168 | text: "Username" 169 | theme_text_color: "Custom" 170 | text_color_normal: [0, 0, 0, 0.7] 171 | 172 | MDTextField: 173 | mode: "filled" 174 | theme_line_color: "Custom" 175 | line_color_normal: [1, 1, 1, 0] 176 | 177 | MDTextFieldLeadingIcon: 178 | icon: "lock" 179 | theme_icon_color: "Custom" 180 | icon_color_normal: app.theme_cls.primaryColor 181 | icon_color_focus: app.theme_cls.primaryColor 182 | 183 | MDTextFieldHintText: 184 | text: "Password" 185 | theme_text_color: "Custom" 186 | text_color_normal: [0, 0, 0, 0.7] 187 | 188 | MDTextFieldTrailingIcon: 189 | icon: "eye" 190 | theme_icon_color: "Custom" 191 | icon_color_normal: app.theme_cls.primaryColor 192 | icon_color_focus: app.theme_cls.primaryColor 193 | 194 | MDExtendedFabButton: 195 | pos_hint: {"center_x": 0.5} 196 | size_hint: 1, None 197 | theme_bg_color: "Custom" 198 | fab_state: "expand" 199 | md_bg_color: app.theme_cls.secondaryFixedDimColor 200 | 201 | MDExtendedFabButtonText: 202 | text: "Login" 203 | theme_font_size: "Custom" 204 | font_size: "19sp" 205 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Home/1/extensions.py: -------------------------------------------------------------------------------- 1 | from kivymd.app import MDApp 2 | from kivymd.uix.navigationbar import MDNavigationBar, MDNavigationItem 3 | from kivymd.uix.screenmanager import MDScreenManager 4 | from kivy.properties import StringProperty 5 | 6 | napp = MDApp.get_running_app() 7 | 8 | 9 | def on_switch_tabs( 10 | instance: object, 11 | bar: MDNavigationBar, 12 | item_icon: str, 13 | item_text: str, 14 | sub_manager_screens: MDScreenManager, 15 | ) -> None: 16 | sub_manager_screens.current = item_text.lower() 17 | 18 | 19 | class BaseNavigationItem(MDNavigationItem): 20 | icon = StringProperty() 21 | text = StringProperty() 22 | ripple_effect = False 23 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Home/1/home_screen.kv: -------------------------------------------------------------------------------- 1 | #: import on_switch_tabs View.{{parsed_name}}.extensions.on_switch_tabs 2 | #: import BaseNavigationItem View.{{parsed_name}}.extensions.BaseNavigationItem 3 | #: import SAT kivymd.uix.transition.MDSharedAxisTransition 4 | 5 | <{{parsed_name}}View>: 6 | MDScreenManager: 7 | id: sub_manager_screens 8 | transition: SAT() 9 | 10 | MDScreen: 11 | name: "home" 12 | 13 | MDBoxLayout: 14 | orientation: "vertical" 15 | 16 | MDLabel: 17 | text: "Home" 18 | style: "Body" 19 | role: "large" 20 | halign: "center" 21 | 22 | MDScreen: 23 | name: "login" 24 | 25 | MDBoxLayout: 26 | orientation: "vertical" 27 | 28 | MDLabel: 29 | text: "Login" 30 | style: "Body" 31 | role: "large" 32 | halign: "center" 33 | 34 | MDScreen: 35 | name: "settings" 36 | 37 | MDBoxLayout: 38 | orientation: "vertical" 39 | 40 | MDLabel: 41 | text: "Settings" 42 | style: "Body" 43 | role: "large" 44 | halign: "center" 45 | 46 | MDNavigationBar: 47 | on_switch_tabs: on_switch_tabs(*args, sub_manager_screens) 48 | 49 | BaseNavigationItem: 50 | text: "Home" 51 | icon: "home" 52 | active: True 53 | 54 | BaseNavigationItem: 55 | text: "Login" 56 | icon: "login" 57 | 58 | BaseNavigationItem: 59 | text: "Settings" 60 | icon: "cog" 61 | 62 | MDStackLayout: 63 | size_hint: 1, 1 64 | 65 | MDBoxLayout: 66 | adaptive_height: True 67 | padding: [20, 20, 20, 20] 68 | spacing: 15 69 | 70 | MDIconButton: 71 | icon: "menu" 72 | style: "tonal" 73 | color_map: "secodary" 74 | theme_bg_color: "Custom" 75 | md_bg_color: [0.5, 0.6, 1, 0.1] 76 | 77 | : 78 | 79 | MDNavigationItemIcon: 80 | icon: root.icon 81 | 82 | MDNavigationItemLabel: 83 | text: root.text 84 | text_color_active: app.theme_cls.onPrimaryFixedColor 85 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Home/2/extensions.py: -------------------------------------------------------------------------------- 1 | from kivymd.app import MDApp 2 | from kivymd.uix.navigationrail import MDNavigationRailItem 3 | from kivy.properties import StringProperty 4 | 5 | napp = MDApp.get_running_app() 6 | 7 | 8 | class BaseNavigationRailItem(MDNavigationRailItem): 9 | icon = StringProperty() 10 | text = StringProperty() 11 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Home/2/home_screen.kv: -------------------------------------------------------------------------------- 1 | #: import BaseNavigationRailItem View.{{parsed_name}}.extensions.BaseNavigationRailItem 2 | 3 | <{{parsed_name}}View>: 4 | MDStackLayout: 5 | size_hint:1,1 6 | 7 | MDLabel: 8 | text:"{{parsed_name}}" 9 | font_style:"Body" 10 | role:"large" 11 | halign:"center" 12 | 13 | MDNavigationRail: 14 | type: "labeled" 15 | anchor:"top" 16 | md_bg_color:app.theme_cls.surfaceContainerHighColor 17 | 18 | MDNavigationRailMenuButton: 19 | icon: "menu" 20 | 21 | BaseNavigationRailItem: 22 | icon: "home-variant-outline" 23 | text: "Home" 24 | active: True 25 | 26 | BaseNavigationRailItem: 27 | icon: "message-text-outline" 28 | text: "Chats" 29 | 30 | BaseNavigationRailItem: 31 | icon: "phone-log-outline" 32 | text: "Calls" 33 | 34 | BaseNavigationRailItem: 35 | icon: "bell-outline" 36 | text: "Notifications" 37 | 38 | BaseNavigationRailItem: 39 | icon: "account-outline" 40 | text: "Account" 41 | 42 | : 43 | 44 | MDNavigationRailItemIcon: 45 | icon: root.icon 46 | theme_font_size:"Custom" 47 | font_size:"25sp" 48 | 49 | MDNavigationRailItemLabel: 50 | text: root.text 51 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Home/3/home_screen.kv: -------------------------------------------------------------------------------- 1 | <{{parsed_name}}View>: 2 | MDNavigationLayout: 3 | MDScreenManager: 4 | MDScreen: 5 | MDStackLayout: 6 | size_hint: 1, 1 7 | 8 | MDBoxLayout: 9 | adaptive_height: True 10 | padding: [20, 20, 20, 20] 11 | spacing: 15 12 | 13 | MDIconButton: 14 | icon: "menu" 15 | style: "standard" 16 | theme_icon_color: "Custom" 17 | icon_color: "midnightblue" 18 | 19 | MDLabel: 20 | text: "KvDeveloper" 21 | font_style: "Title" 22 | role: "medium" 23 | adaptive_height: True 24 | theme_text_color: "Custom" 25 | text_color: "midnightblue" 26 | 27 | MDBoxLayout: 28 | adaptive_width: True 29 | MDAnchorLayout: 30 | anchor_x: 'right' 31 | MDGridLayout: 32 | rows: 1 33 | adaptive_size: True 34 | 35 | MDIconButton: 36 | icon: "login" 37 | theme_icon_color: "Custom" 38 | icon_color: "midnightblue" 39 | 40 | MDBoxLayout: 41 | orientation: "vertical" 42 | ScrollView: 43 | always_overscroll: False 44 | {{parsed_name}}Context: 45 | orientation: 'vertical' 46 | padding: [20, 20, 20, 20] 47 | adaptive_height: True 48 | 49 | Widget: 50 | size_hint: 1, None 51 | 52 | #MDNavigationDrawer: 53 | #ContentNavigationDrawer: 54 | 55 | <{{parsed_name}}Context@MDBoxLayout>: 56 | MDBoxLayout: 57 | orientation: "vertical" 58 | adaptive_height: True 59 | padding: [15, 15, 15, 15] 60 | spacing: 15 61 | radius: [25] 62 | md_bg_color: [0, 0, 0, 0.025] 63 | MDBoxLayout: 64 | adaptive_height: True 65 | padding: [15, 15, 15, 15] 66 | MDBoxLayout: 67 | adaptive_height: True 68 | orientation: "vertical" 69 | MDLabel: 70 | text: "KvDeveloper" 71 | font_style: "Headline" 72 | role: "medium" 73 | theme_text_color: "Custom" 74 | text_color: "midnightblue" 75 | 76 | MDBoxLayout: 77 | orientation: 'vertical' 78 | adaptive_size: True 79 | MDAnchorLayout: 80 | anchor_x: 'right' 81 | anchor_y: 'top' 82 | size_hint_y: None 83 | height: avatar.height 84 | 85 | FitImage: 86 | id: avatar 87 | source: f"{app.image_library_path}/kvdeveloper/kvdeveloper_logo128.png" 88 | size_hint: None, None 89 | size: "95sp", "95sp" 90 | MDDivider: 91 | MDBoxLayout: 92 | orientation: 'vertical' 93 | adaptive_height: True 94 | spacing: 15 95 | padding: [15, 15, 15, 15] 96 | 97 | MDLabel: 98 | multiline: True 99 | theme_font_size: "Custom" 100 | font_size: "16sp" 101 | adaptive_height: True 102 | text: 103 | "KvDeveloper is a PyPI module designed to streamline the development of Kivy and KivyMD applications. Inspired by Expo CLI for React Native, it offers starter templates and essential functionalities to kickstart your projects with ease. With features like predefined templates, MVC architecture support, and customizable options, KvDeveloper simplifies creating robust and organized Kivy projects." 104 | 105 | MDDivider: 106 | MDGridLayout: 107 | cols: 2 108 | adaptive_height: True 109 | padding: [15, 15, 15, 15] 110 | spacing: 15 111 | 112 | MDAnchorLayout: 113 | md_bg_color: [0, 0.4, 1, 0.05] 114 | radius: [15] 115 | size_hint_y: None 116 | 117 | MDIconButton: 118 | icon: "youtube" 119 | theme_icon_color: "Custom" 120 | icon_color: "red" 121 | theme_font_size: "Custom" 122 | size_hint: 1, 1 123 | font_size: "75sp" 124 | on_press: 125 | app.web_open("https://youtube.com/@NovfensecInc") 126 | 127 | MDAnchorLayout: 128 | md_bg_color: [0, 0.4, 1, 0.05] 129 | radius: [15] 130 | size_hint_y: None 131 | 132 | MDIconButton: 133 | icon: "github" 134 | theme_icon_color: "Custom" 135 | icon_color: "black" 136 | theme_font_size: "Custom" 137 | size_hint: 1, 1 138 | font_size: "75sp" 139 | on_press: 140 | app.web_open("https://github.com/Novfensec/KvDeveloper") 141 | 142 | MDAnchorLayout: 143 | md_bg_color: [0, 0.4, 1, 0.05] 144 | radius: [15] 145 | size_hint_y: None 146 | 147 | MDIconButton: 148 | icon: "blank" 149 | theme_font_size: "Custom" 150 | size_hint: 1, 1 151 | font_size: "75sp" 152 | on_press: 153 | app.web_open("https://discord.com/invite/U9bfkD6A4c") 154 | 155 | FitImage: 156 | source: f"{app.image_library_path}/basic/discord128.png" 157 | size_hint: None, None 158 | size: "85dp", "85dp" 159 | 160 | MDAnchorLayout: 161 | md_bg_color: [0, 0.4, 1, 0.05] 162 | radius: [15] 163 | size_hint_y: None 164 | 165 | MDIconButton: 166 | icon: "blank" 167 | theme_font_size: "Custom" 168 | size_hint: 1, 1 169 | font_size: "75sp" 170 | on_press: 171 | app.web_open("https://stackoverflow.com/users/16486510/novfensec") 172 | 173 | FitImage: 174 | source: f"{app.image_library_path}/basic/stack_overflow128.png" 175 | size_hint: None, None 176 | size: "75dp", "75dp" 177 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Settings/1/extensions.py: -------------------------------------------------------------------------------- 1 | from kivymd.uix.relativelayout import MDRelativeLayout 2 | from kivy.properties import StringProperty, BooleanProperty 3 | 4 | 5 | class IconListItem(MDRelativeLayout): 6 | text = StringProperty() 7 | icon = StringProperty() 8 | secondary_text = StringProperty() 9 | ripple = BooleanProperty(True) 10 | -------------------------------------------------------------------------------- /kvdeveloper/layouts/Settings/1/settings_screen.kv: -------------------------------------------------------------------------------- 1 | #: import IconListItem View.{{parsed_name}}.extensions.IconListItem 2 | 3 | <{{parsed_name}}View>: 4 | MDStackLayout: 5 | size_hint: 1, 1 6 | 7 | MDBoxLayout: 8 | orientation: 'vertical' 9 | adaptive_height: True 10 | padding: [20, 20, 20, 20] 11 | spacing: 15 12 | 13 | 14 | MDIconButton: 15 | icon: "arrow-left" 16 | style: "tonal" 17 | color_map: "secodary" 18 | theme_bg_color: "Custom" 19 | md_bg_color: [0.5, 0.6, 1, 0.1] 20 | on_press: 21 | app.referrer() 22 | 23 | MDScrollView: 24 | {{parsed_name}}Context: 25 | orientation: 'vertical' 26 | adaptive_height: True 27 | padding: [20, 20, 20, 20] 28 | spacing: 15 29 | 30 | <{{parsed_name}}Context@MDBoxLayout>: 31 | MDBoxLayout: 32 | orientation: "vertical" 33 | adaptive_height: True 34 | radius: [25] 35 | 36 | MDTextField: 37 | mode: "filled" 38 | theme_line_color: "Custom" 39 | line_color_normal: [1, 1, 1, 0] 40 | 41 | MDTextFieldLeadingIcon: 42 | icon: "magnify" 43 | theme_icon_color: "Custom" 44 | icon_color_normal: app.theme_cls.primaryColor 45 | icon_color_focus: app.theme_cls.primaryColor 46 | 47 | MDTextFieldHintText: 48 | text: "Search" 49 | theme_text_color: "Custom" 50 | text_color_normal: app.theme_cls.outlineColor 51 | 52 | MDTextFieldTrailingIcon: 53 | icon: "microphone" 54 | theme_icon_color: "Custom" 55 | icon_color_normal: app.theme_cls.primaryColor 56 | icon_color_focus: app.theme_cls.primaryColor 57 | 58 | MDBoxLayout: 59 | orientation: "vertical" 60 | adaptive_height: True 61 | padding: [0, 10, 0, 10] 62 | spacing: 15 63 | 64 | MDList: 65 | radius: [15] 66 | spacing: 10 67 | cols: 1 68 | 69 | IconListItem: 70 | icon: "weather-night" 71 | text: "Night Mode" 72 | ripple: False 73 | secondary_text: "Enabled" if mode_switch.active else "Disabled" 74 | 75 | MDSwitch: 76 | id: mode_switch 77 | pos_hint: {"right": 0.95, "center_y": 0.5} 78 | on_active: 79 | app.apply_styles('Dark') if self.active else app.apply_styles('Light') 80 | 81 | IconListItem: 82 | icon: "bell-outline" 83 | text: "Notifications" 84 | ripple: False 85 | secondary_text: "News and updates" 86 | 87 | MDSwitch: 88 | id: notification_switch 89 | pos_hint: {"right": 0.95, "center_y": 0.5} 90 | 91 | MDDivider: 92 | MDList: 93 | radius: [15] 94 | spacing: 10 95 | cols: 1 96 | 97 | IconListItem: 98 | icon: "magic-staff" 99 | text: "Appearance" 100 | secondary_text: "Themes, layout, text size" 101 | 102 | IconListItem: 103 | icon: "shield-lock-outline" 104 | text: "Privacy and security" 105 | secondary_text: "Terms of use and privacy policy" 106 | 107 | IconListItem: 108 | icon: "help-circle-outline" 109 | text: "Help and feedback" 110 | secondary_text: "FAQs, report problems, social" 111 | 112 | IconListItem: 113 | icon: "information-outline" 114 | text: "About KvDeveloper" 115 | secondary_text: "Updates, version" 116 | 117 | : 118 | size_hint: 1, None 119 | height: list_item.height 120 | md_bg_color: app.theme_cls.surfaceContainerLowColor 121 | radius: [15] 122 | padding: [5, 0, 5, 0] 123 | 124 | MDListItem: 125 | id: list_item 126 | theme_bg_color: "Custom" 127 | md_bg_color: 1, 1, 1, 0 128 | ripple_effect: root.ripple 129 | 130 | MDListItemLeadingIcon: 131 | icon: root.icon 132 | color: app.theme_cls.onPrimaryFixedVariantColor 133 | theme_bg_color: "Custom" 134 | md_bg_color: app.theme_cls.inversePrimaryColor 135 | 136 | MDListItemHeadlineText: 137 | text: root.text 138 | 139 | MDListItemSupportingText: 140 | text: root.secondary_text 141 | -------------------------------------------------------------------------------- /kvdeveloper/libs/PdfViewer/Android/__init__.py: -------------------------------------------------------------------------------- 1 | from jnius import autoclass # type: ignore 2 | from android.runnable import run_on_ui_thread # type: ignore 3 | 4 | 5 | @run_on_ui_thread 6 | def open_pdf_on_android(filepath: str, *args) -> None: 7 | try: 8 | Intent = autoclass("android.content.Intent") 9 | Uri = autoclass("android.net.Uri") 10 | File = autoclass("java.io.File") 11 | file = File(filepath) 12 | uri = Uri.fromFile(file) 13 | 14 | intent = Intent(Intent.ACTION_VIEW) 15 | intent.setDataAndType(uri, "application/pdf") 16 | intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) 17 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 18 | 19 | PythonActivity = autoclass("org.kivy.android.PythonActivity") 20 | current_activity = PythonActivity.mActivity 21 | current_activity.startActivity(intent) 22 | except Exception as e: 23 | print(f"Failed to open PDF: {e}") 24 | -------------------------------------------------------------------------------- /kvdeveloper/libs/PdfViewer/__init__.py: -------------------------------------------------------------------------------- 1 | from kivy.utils import platform 2 | 3 | 4 | class PdfViewer: 5 | def __init__(self, *args, **kwargs): 6 | super().__init__(*args, **kwargs) 7 | 8 | def open_new_file(self, filepath: str, *args) -> None: 9 | 10 | if platform == "android": 11 | from .Android import open_pdf_on_android 12 | open_pdf_on_android(filepath=filepath) 13 | 14 | elif platform == "ios": 15 | from .iOS import open_pdf_on_ios 16 | open_pdf_on_ios(filepath=filepath) 17 | 18 | else: 19 | raise NotImplementedError 20 | -------------------------------------------------------------------------------- /kvdeveloper/libs/PdfViewer/iOS/__init__.py: -------------------------------------------------------------------------------- 1 | def open_pdf_on_ios(filepath: str, *args) -> None: 2 | return -------------------------------------------------------------------------------- /kvdeveloper/libs/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import List 3 | 4 | from kvdeveloper.config import LIBS_DIR 5 | from kvdeveloper.module import console 6 | from kvdeveloper.utils import replace_placeholders 7 | 8 | 9 | def add_from_libs(name_libs: List[str], destination: str) -> None: 10 | for libs in name_libs: 11 | libs_path = os.path.join(LIBS_DIR, libs) 12 | destination = os.path.join(destination, libs) 13 | 14 | # Walk through the libs directory and replicate structure in destination 15 | for root, _, files in os.walk(libs_path): 16 | relative_path = os.path.relpath(root, libs_path) # Relative path from the libs 17 | target_dir = os.path.join( 18 | destination, relative_path 19 | ) # Corresponding destination path 20 | 21 | os.makedirs(target_dir, exist_ok=True) # Ensure the target directory exists 22 | 23 | # Process files in the current directory 24 | for file_name in files: 25 | # Skip unnecessary file types 26 | if file_name.endswith((".pyc", ".pyo")): 27 | continue 28 | 29 | source_file = os.path.join(root, file_name) # Full path to the source file 30 | target_file = os.path.join( 31 | target_dir, file_name 32 | ) # Full path to the target file 33 | 34 | # Read and process the content of the template file 35 | with open(source_file, "r", encoding="utf-8") as src: 36 | content = src.read() 37 | 38 | # Replace placeholders in the content 39 | # content = replace_placeholders(content, variables) 40 | 41 | # Write the processed content to the target file 42 | with open(target_file, "w", encoding="utf-8") as dest: 43 | dest.write(content) 44 | 45 | console.print(f"\nCreated file: [bright_white]{target_file}[/bright_white]") 46 | -------------------------------------------------------------------------------- /kvdeveloper/structures/MVC/Controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/structures/MVC/Controller/__init__.py -------------------------------------------------------------------------------- /kvdeveloper/structures/MVC/Model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/structures/MVC/Model/__init__.py -------------------------------------------------------------------------------- /kvdeveloper/structures/MVC/Model/base_model.py: -------------------------------------------------------------------------------- 1 | # The model implements the observer pattern. This means that the class must 2 | # support adding, removing, and alerting observers. In this case, the model is 3 | # completely independent of controllers and views. It is important that all 4 | # registered observers implement a specific method that will be called by the 5 | # model when they are notified (in this case, it is the `model_is_changed` 6 | # method). For this, observers must be descendants of an abstract class, 7 | # inheriting which, the `model_is_changed` method must be overridden. 8 | 9 | 10 | class BaseScreenModel: 11 | """Implements a base class for model modules.""" 12 | 13 | _observers = [] 14 | 15 | def add_observer(self, observer) -> None: 16 | self._observers.append(observer) 17 | 18 | def remove_observer(self, observer) -> None: 19 | self._observers.remove(observer) 20 | 21 | def notify_observers(self, name_screen: str) -> None: 22 | """ 23 | Method that will be called by the observer when the model data changes. 24 | 25 | :param name_screen: 26 | name of the view for which the method should be called 27 | :meth:`model_is_changed`. 28 | """ 29 | 30 | for observer in self._observers: 31 | if observer.name == name_screen: 32 | observer.model_is_changed() 33 | break 34 | -------------------------------------------------------------------------------- /kvdeveloper/structures/MVC/Utility/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/structures/MVC/Utility/__init__.py -------------------------------------------------------------------------------- /kvdeveloper/structures/MVC/Utility/observer.py: -------------------------------------------------------------------------------- 1 | # Of course, "very flexible Python" allows you to do without an abstract 2 | # superclass at all or use the clever exception `NotImplementedError`. In my 3 | # opinion, this can negatively affect the architecture of the application. 4 | # I would like to point out that using Kivy, one could use the on-signaling 5 | # model. In this case, when the state changes, the model will send a signal 6 | # that can be received by all attached observers. This approach seems less 7 | # universal - you may want to use a different library in the future. 8 | 9 | 10 | class Observer: 11 | """Abstract superclass for all observers.""" 12 | 13 | def model_is_changed(self): 14 | """ 15 | The method that will be called on the observer when the model changes. 16 | """ 17 | -------------------------------------------------------------------------------- /kvdeveloper/structures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/structures/__init__.py -------------------------------------------------------------------------------- /kvdeveloper/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Novfensec/KvDeveloper/af6a8ea5e1ca1390aef770aabff22946b460e95b/kvdeveloper/templates/__init__.py -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/README.md: -------------------------------------------------------------------------------- 1 | # KivyMD Blank starter template 2 | A simple app using KivyMD. 3 | 4 | ## Overview 5 | This repository contains a basic KivyMD app with hot reload functionality. You can make code changes and instantly see the updates without restarting the application [ *Only for changes made to `main.py` and `.kv` files.* ]. 6 | 7 | ## Prerequisites 8 | Make sure you have the following installed: 9 | 10 | + **Kivy** : A Python framework for creating cross-platform applications. 11 | + **KivyMD** : A library providing Material Design components for Kivy. 12 | 13 | ## Installation 14 | 1. Install `Kivy=2.3.0(specific)`, `KivyMD=1.1.1(specific)` 15 | 16 |
pip install kivy==2.3.0 kivymd==1.1.1
17 | 18 | ## Usage 19 | 1. Run the KivyMD app using terminal: 20 | 21 | - Activate Virtual Environment. 22 | 23 |
venv\scripts\activate
24 | 25 | - Run the App. 26 | 27 |
(venv) python main.py
28 | 29 | 2. Simply execute the `main.py` file from your editor in a python debug terminal. 30 | 31 | You should see the app window open with some basic app design. Make changes to the `.kv` files and save it to see the updates in real-time. 32 | 33 | Happy Coding! 34 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/View/SampleScreen/__init__.py: -------------------------------------------------------------------------------- 1 | from .sample_screen import SampleScreenView 2 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/View/SampleScreen/sample_screen.kv: -------------------------------------------------------------------------------- 1 | : 2 | MDLabel: 3 | text: "Sample Screen" 4 | color:app.theme_cls.primaryColor 5 | halign:"center" 6 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/View/SampleScreen/sample_screen.py: -------------------------------------------------------------------------------- 1 | from View.base_screen import BaseScreenView 2 | 3 | 4 | class SampleScreenView(BaseScreenView): 5 | pass 6 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/View/base_screen.kv: -------------------------------------------------------------------------------- 1 | : 2 | md_bg_color: app.theme_cls.backgroundColor -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/View/base_screen.py: -------------------------------------------------------------------------------- 1 | from kivy.properties import ObjectProperty 2 | from kivymd.app import MDApp 3 | from kivymd.uix.screen import MDScreen 4 | 5 | 6 | class BaseScreenView(MDScreen): 7 | 8 | manager_screens = ObjectProperty() 9 | """ 10 | Screen manager object - :class:`~kivymd.uix.screenmanager.MDScreenManager`. 11 | 12 | :attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty` 13 | and defaults to `None`. 14 | """ 15 | 16 | def __init__(self, *args, **kwargs): 17 | super().__init__(*args, **kwargs) 18 | # Often you need to get access to the application object from the view 19 | # class. You can do this using this attribute. 20 | self.app = MDApp.get_running_app() 21 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/View/screens.py: -------------------------------------------------------------------------------- 1 | from View.SampleScreen.sample_screen import SampleScreenView 2 | 3 | screens = { 4 | "sample screen": { 5 | "object": SampleScreenView, 6 | "module": "View.SampleScreen", 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import webbrowser 3 | from kivymd.app import MDApp 4 | from kivymd.uix.screenmanager import MDScreenManager 5 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 6 | from kivymd.utils.set_bars_colors import set_bars_colors 7 | from kivy.core.window import Window 8 | from kivy.clock import Clock 9 | 10 | 11 | def set_softinput(*args) -> None: 12 | Window.keyboard_anim_args = {"d": 0.2, "t": "in_out_expo"} 13 | Window.softinput_mode = "below_target" 14 | 15 | 16 | Window.on_restore(Clock.schedule_once(set_softinput, 0.1)) 17 | 18 | 19 | class UI(MDScreenManager): 20 | def __init__(self, *args, **kwargs): 21 | super(UI, self).__init__(*args, **kwargs) 22 | self.transition = SAT() 23 | 24 | 25 | class {{project_name}}(MDApp): 26 | def __init__(self, *args, **kwargs): 27 | super({{project_name}}, self).__init__(*args, **kwargs) 28 | self.load_all_kv_files(os.path.join(self.directory, "View")) 29 | self.theme_cls.primary_palette = "Midnightblue" 30 | self.manager_screens = UI() 31 | 32 | def build(self) -> UI: 33 | self.generate_application_screens() 34 | self.apply_styles() 35 | return self.manager_screens 36 | 37 | def generate_application_screens(self) -> None: 38 | # adds different screen widgets to the screen manager 39 | import View.screens 40 | 41 | screens = View.screens.screens 42 | 43 | for i, name_screen in enumerate(screens.keys()): 44 | view = screens[name_screen]["object"]() 45 | view.manager_screens = self.manager_screens 46 | view.name = name_screen 47 | self.manager_screens.add_widget(view) 48 | 49 | def apply_styles(self, style: str = "Light") -> None: 50 | self.theme_cls.theme_style = style 51 | Window.clearcolor = self.theme_cls.backgroundColor 52 | if style == "Light": 53 | style = "Dark" 54 | self.set_bars_colors(style) 55 | 56 | def set_bars_colors(self, style: str = "Light") -> None: 57 | set_bars_colors( 58 | self.theme_cls.primaryColor, # status bar color 59 | self.theme_cls.primaryColor, # navigation bar color 60 | style, # icons color of status bar 61 | ) 62 | 63 | def referrer(self, destination: str = None) -> None: 64 | if self.manager_screens.current != destination: 65 | self.manager_screens.current = destination 66 | 67 | def web_open(self, url: str) -> None: 68 | webbrowser.open_new_tab(url) 69 | 70 | 71 | if __name__ == "__main__": 72 | {{project_name}}().run() 73 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/mainh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | from kivy import Config 4 | from PIL import ImageGrab 5 | 6 | resolution = ImageGrab.grab().size 7 | 8 | # Change the values of the application window size as you need. 9 | Config.set("graphics", "height", "690") 10 | Config.set("graphics", "width", "317") 11 | 12 | from kivy.core.window import Window 13 | 14 | # Place the application window on the right side of the computer screen. 15 | Window.top = 30 16 | Window.left = resolution[0] - Window.width + 5 17 | 18 | import webbrowser 19 | from kivymd.tools.hotreload.app import MDApp 20 | from kivymd.uix.screenmanager import MDScreenManager 21 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 22 | 23 | 24 | class UI(MDScreenManager): 25 | def __init__(self, *args, **kwargs): 26 | super(UI, self).__init__(*args, **kwargs) 27 | self.transition = SAT() 28 | 29 | 30 | class {{project_name}}(MDApp): 31 | def __init__(self, *args, **kwargs): 32 | super({{project_name}}, self).__init__(*args, **kwargs) 33 | self.DEBUG = True 34 | self.KV_DIRS = [ 35 | os.path.join(os.getcwd(), "View"), 36 | ] 37 | self.theme_cls.primary_palette = "Midnightblue" 38 | 39 | def build_app(self) -> UI: 40 | self.manager_screens = UI() 41 | self.generate_application_screens() 42 | return self.manager_screens 43 | 44 | def generate_application_screens(self) -> None: 45 | """ 46 | Adds different screen widgets to the screen manager 47 | """ 48 | import View.screens 49 | 50 | importlib.reload(View.screens) 51 | screens = View.screens.screens 52 | 53 | for i, name_screen in enumerate(screens.keys()): 54 | view = screens[name_screen]["object"]() 55 | view.manager_screens = self.manager_screens 56 | view.name = name_screen 57 | self.manager_screens.add_widget(view) 58 | 59 | def apply_styles(self, style: str = "Light") -> None: 60 | self.theme_cls.theme_style = style 61 | Window.clearcolor = self.theme_cls.backgroundColor 62 | 63 | def referrer(self, destination: str = None) -> None: 64 | if self.manager_screens.current != destination: 65 | self.manager_screens.current = destination 66 | 67 | def web_open(self, url: str) -> None: 68 | webbrowser.open_new_tab(url) 69 | 70 | 71 | if __name__ == "__main__": 72 | {{project_name}}().run() 73 | -------------------------------------------------------------------------------- /kvdeveloper/templates/blank/requirements.txt: -------------------------------------------------------------------------------- 1 | kivy>=2.0.0 2 | https://github.com/kivymd/KivyMD/archive/master.zip 3 | watchdog>=4.0.1 4 | pillow>=10.0.0 5 | kvdeveloper>={{kvdeveloper_version}} -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/README.md: -------------------------------------------------------------------------------- 1 | # KivyMD nav_dock starter template 2 | A simple app using KivyMD with Navigation drawer, BottomNavigation, HomeScreen, LoginScreen and SettingsScreen components. 3 | 4 | ## Overview 5 | This repository contains a basic KivyMD app with Navigation drawer, BottomNavigation, HomeScreen, LoginScreen and SettingsScreen components including hot reload functionality. You can make code changes and instantly see the updates without restarting the application [ *Only for changes made to `main.py` and `.kv` files.* ]. 6 | 7 | 8 | 9 | ## Prerequisites 10 | Make sure you have the following installed: 11 | 12 | + **Kivy** : A Python framework for creating cross-platform applications. 13 | + **KivyMD** : A library providing Material Design components for Kivy. 14 | 15 | ## Installation 16 | 1. Install `Kivy=2.3.0(specific)`, `KivyMD=1.1.1(specific)` 17 | 18 |
pip install kivy==2.3.0 kivymd==1.1.1
19 | 20 | ## Usage 21 | 1. Run the Kivy app using terminal: 22 | 23 | - Activate Virtual Environment. 24 | 25 |
venv\scripts\activate
26 | 27 | - Run the App. 28 | 29 |
(venv) python main.py
30 | 31 | 2. Simply execute the `main.py` file from your editor. 32 | 33 | You should see the app window open with some basic app design. Make changes to the `.kv` files and save it to see the updates in real-time. 34 | 35 | Happy Coding! 36 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/HomeScreen/__init__.py: -------------------------------------------------------------------------------- 1 | from .home_screen import HomeScreenView 2 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/HomeScreen/extensions.py: -------------------------------------------------------------------------------- 1 | from kivymd.app import MDApp 2 | from kivymd.uix.navigationbar import MDNavigationBar, MDNavigationItem 3 | from kivy.properties import StringProperty 4 | 5 | napp = MDApp.get_running_app() 6 | 7 | 8 | def on_switch_tabs( 9 | self, bar: MDNavigationBar, item_icon: str, item_text: str, sub_manager_screens 10 | ): 11 | sub_manager_screens.current = item_text.lower() 12 | 13 | 14 | class BaseNavigationItem(MDNavigationItem): 15 | icon = StringProperty() 16 | text = StringProperty() 17 | ripple_effect = False 18 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/HomeScreen/home_screen.py: -------------------------------------------------------------------------------- 1 | from View.base_screen import BaseScreenView 2 | 3 | 4 | class HomeScreenView(BaseScreenView): 5 | pass 6 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/base_screen.kv: -------------------------------------------------------------------------------- 1 | #: import DrawerClickableItem View.extensions.DrawerClickableItem 2 | #: import CustomTextField View.extensions.CustomTextField 3 | 4 | : 5 | md_bg_color: app.theme_cls.surfaceColor 6 | 7 | 8 | md_bg_color: app.theme_cls.primaryContainerColor 9 | ripple_color: app.theme_cls.inversePrimaryColor 10 | inactive_indicator_color: app.theme_cls.primaryContainerColor 11 | active_indicator_color: app.theme_cls.inversePrimaryColor 12 | 13 | MDNavigationDrawerItemLeadingIcon: 14 | icon: root.icon 15 | 16 | MDNavigationDrawerItemText: 17 | text: root.text 18 | 19 | : 20 | spacing: 10 21 | MDNavigationDrawerHeader: 22 | orientation: "vertical" 23 | adaptive_height: True 24 | 25 | MDLabel: 26 | text: "KvDeveloper" 27 | theme_text_color: "Custom" 28 | theme_line_height: "Custom" 29 | line_height: 0 30 | text_color: app.theme_cls.onPrimaryFixedColor 31 | adaptive_height: True 32 | font_style: "Display" 33 | role: "small" 34 | 35 | MDNavigationDrawerDivider: 36 | MDBoxLayout: 37 | adaptive_height: True 38 | orientation: "vertical" 39 | padding: [10, 10, 10, 10] 40 | spacing: 15 41 | 42 | FitImage: 43 | id: avatar 44 | source: f"{app.image_library_path}/kvdeveloper/kvdeveloper_logo128.png" 45 | size_hint: None, None 46 | size: "100sp", "100sp" 47 | Widget: 48 | 49 | MDLabel: 50 | text: 'Novfensec Inc.' 51 | color: app.theme_cls.primaryColor 52 | style: "Body" 53 | role: "large" 54 | 55 | Widget: 56 | 57 | MDNavigationDrawerDivider: 58 | 59 | DrawerClickableItem: 60 | icon: "source-pull" 61 | text: "Screen 1" 62 | 63 | DrawerClickableItem: 64 | icon: "source-branch" 65 | text: "Screen 2" 66 | 67 | DrawerClickableItem: 68 | icon: "source-repository-multiple" 69 | text: "Screen 3" 70 | 71 | : 72 | helper_text: "" 73 | hint_text: "" 74 | icon_left: "blank" 75 | mode: "filled" 76 | theme_bg_color: "Custom" 77 | fill_color_normal: app.theme_cls.transparentColor 78 | fill_color_focus: self.fill_color_normal 79 | theme_line_color: "Custom" 80 | line_color_normal: app.theme_cls.transparentColor 81 | line_color_focus: self.fill_color_normal 82 | radius: [10] 83 | 84 | MDTextFieldLeadingIcon: 85 | icon: root.icon_left 86 | theme_icon_color: "Custom" 87 | icon_color_normal: app.theme_cls.primaryColor 88 | icon_color_focus: app.theme_cls.primaryColor 89 | 90 | MDTextFieldHintText: 91 | text: root.hint_text 92 | theme_text_color: "Custom" 93 | text_color_normal: [0, 0, 0, 0.6] 94 | 95 | MDTextFieldHelperText: 96 | text: root.helper_text 97 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/base_screen.py: -------------------------------------------------------------------------------- 1 | from kivy.properties import ObjectProperty 2 | from kivymd.app import MDApp 3 | from kivymd.uix.screen import MDScreen 4 | 5 | 6 | class BaseScreenView(MDScreen): 7 | 8 | manager_screens = ObjectProperty() 9 | """ 10 | Screen manager object - :class:`~kivymd.uix.screenmanager.MDScreenManager`. 11 | 12 | :attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty` 13 | and defaults to `None`. 14 | """ 15 | 16 | def __init__(self, *args, **kwargs): 17 | super().__init__(*args, **kwargs) 18 | # Often you need to get access to the application object from the view 19 | # class. You can do this using this attribute. 20 | self.app = MDApp.get_running_app() 21 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/extensions.py: -------------------------------------------------------------------------------- 1 | from kivymd.uix.navigationdrawer import MDNavigationDrawerItem 2 | from kivymd.uix.textfield import MDTextField 3 | from kivy.properties import StringProperty 4 | 5 | 6 | class DrawerClickableItem(MDNavigationDrawerItem): 7 | icon = StringProperty() 8 | text = StringProperty() 9 | 10 | 11 | class CustomTextField(MDTextField): 12 | icon_left = StringProperty() 13 | hint_text = StringProperty() 14 | helper_text = StringProperty() 15 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/View/screens.py: -------------------------------------------------------------------------------- 1 | from View.HomeScreen.home_screen import HomeScreenView 2 | 3 | screens = { 4 | "home screen": { 5 | "object": HomeScreenView, 6 | "module": "View.HomeScreen", 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import webbrowser 3 | from kivymd.app import MDApp 4 | from kivymd.uix.screenmanager import MDScreenManager 5 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 6 | from kvdeveloper.config import IMAGE_LIBRARY 7 | from kivymd.utils.set_bars_colors import set_bars_colors 8 | from kivy.core.window import Window 9 | from kivy.clock import Clock 10 | 11 | Clock.max_iteration=30 12 | 13 | 14 | def set_softinput(*args) -> None: 15 | Window.keyboard_anim_args = {"d": 0.2, "t": "in_out_expo"} 16 | Window.softinput_mode = "below_target" 17 | 18 | 19 | Window.on_restore(Clock.schedule_once(set_softinput, 0.1)) 20 | 21 | 22 | class UI(MDScreenManager): 23 | def __init__(self, *args, **kwargs): 24 | super(UI, self).__init__(*args, **kwargs) 25 | self.transition = SAT() 26 | 27 | 28 | class {{project_name}}(MDApp): 29 | def __init__(self, *args, **kwargs): 30 | super({{project_name}}, self).__init__(*args, **kwargs) 31 | self.load_all_kv_files(os.path.join(self.directory, "View")) 32 | self.theme_cls.primary_palette = "Midnightblue" 33 | self.image_library_path = IMAGE_LIBRARY 34 | self.manager_screens = UI() 35 | 36 | def build(self) -> UI: 37 | self.generate_application_screens() 38 | self.apply_styles() 39 | return self.manager_screens 40 | 41 | def generate_application_screens(self) -> None: 42 | # adds different screen widgets to the screen manager 43 | import View.screens 44 | 45 | screens = View.screens.screens 46 | 47 | for i, name_screen in enumerate(screens.keys()): 48 | view = screens[name_screen]["object"]() 49 | view.manager_screens = self.manager_screens 50 | view.name = name_screen 51 | self.manager_screens.add_widget(view) 52 | 53 | def apply_styles(self, style: str = "Light") -> None: 54 | self.theme_cls.theme_style = style 55 | Window.clearcolor = self.theme_cls.backgroundColor 56 | if style == "Light": 57 | style = "Dark" 58 | self.set_bars_colors(style) 59 | 60 | def set_bars_colors(self, style: str = "Light") -> None: 61 | set_bars_colors( 62 | self.theme_cls.primaryColor, # status bar color 63 | self.theme_cls.primaryColor, # navigation bar color 64 | style, # icons color of status bar 65 | ) 66 | 67 | def referrer(self, destination: str = None) -> None: 68 | if self.manager_screens.current != destination: 69 | self.manager_screens.current = destination 70 | 71 | def web_open(self, url: str) -> None: 72 | webbrowser.open_new_tab(url) 73 | 74 | 75 | if __name__ == "__main__": 76 | {{project_name}}().run() 77 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/mainh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | from kivy import Config 4 | from PIL import ImageGrab 5 | 6 | resolution = ImageGrab.grab().size 7 | 8 | # Change the values of the application window size as you need. 9 | Config.set("graphics", "height", "690") 10 | Config.set("graphics", "width", "317") 11 | 12 | from kivy.core.window import Window 13 | 14 | # Place the application window on the right side of the computer screen. 15 | Window.top = 30 16 | Window.left = resolution[0] - Window.width + 5 17 | 18 | import webbrowser 19 | from kivymd.tools.hotreload.app import MDApp 20 | from kivymd.uix.screenmanager import MDScreenManager 21 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 22 | from kvdeveloper.config import IMAGE_LIBRARY 23 | from kivy.clock import Clock 24 | 25 | Clock.max_iteration = 30 26 | 27 | 28 | class UI(MDScreenManager): 29 | def __init__(self, *args, **kwargs): 30 | super(UI, self).__init__(*args, **kwargs) 31 | self.transition = SAT() 32 | 33 | 34 | class {{project_name}}(MDApp): 35 | def __init__(self, *args, **kwargs): 36 | super({{project_name}}, self).__init__(*args, **kwargs) 37 | self.DEBUG = True 38 | self.KV_DIRS = [ 39 | os.path.join(os.getcwd(), "View"), 40 | ] 41 | self.theme_cls.primary_palette = "Midnightblue" 42 | self.image_library_path = IMAGE_LIBRARY 43 | 44 | def build_app(self) -> UI: 45 | self.apply_styles("Light") 46 | self.manager_screens = UI() 47 | self.generate_application_screens() 48 | return self.manager_screens 49 | 50 | def generate_application_screens(self) -> None: 51 | """ 52 | Adds different screen widgets to the screen manager. 53 | """ 54 | import View.screens 55 | 56 | importlib.reload(View.screens) 57 | screens = View.screens.screens 58 | 59 | for i, name_screen in enumerate(screens.keys()): 60 | view = screens[name_screen]["object"]() 61 | view.manager_screens = self.manager_screens 62 | view.name = name_screen 63 | self.manager_screens.add_widget(view) 64 | 65 | def apply_styles(self, style: str = "Light") -> None: 66 | self.theme_cls.theme_style = style 67 | Window.clearcolor = self.theme_cls.backgroundColor 68 | 69 | def referrer(self, destination: str = None) -> None: 70 | if self.manager_screens.current != destination: 71 | self.manager_screens.current = destination 72 | 73 | def web_open(self, url: str) -> None: 74 | webbrowser.open_new_tab(url) 75 | 76 | 77 | if __name__ == "__main__": 78 | {{project_name}}().run() 79 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_dock/requirements.txt: -------------------------------------------------------------------------------- 1 | kivy>=2.0.0 2 | https://github.com/kivymd/KivyMD/archive/master.zip 3 | watchdog>=4.0.1 4 | pillow>=10.0.0 5 | kvdeveloper>={{kvdeveloper_version}} -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/README.md: -------------------------------------------------------------------------------- 1 | # KivyMD nav_toolbar starter template 2 | A simple app using KivyMD with Navigation drawer, HomeScreen and LoginScreen components. 3 | 4 | ## Overview 5 | This repository contains a basic KivyMD app with Navigation drawer, HomeScreen and LoginScreen components including hot reload functionality. You can make code changes and instantly see the updates without restarting the application [ *Only for changes made to `main.py` and `.kv` files.* ]. 6 | 7 | 8 | 9 | ## Prerequisites 10 | Make sure you have the following installed: 11 | 12 | + **Kivy** : A Python framework for creating cross-platform applications. 13 | + **KivyMD** : A library providing Material Design components for Kivy. 14 | 15 | ## Installation 16 | 1. Install `Kivy=2.3.0(specific)`, `KivyMD=1.1.1(specific)` 17 | 18 |
pip install kivy==2.3.0 kivymd==1.1.1
19 | 20 | ## Usage 21 | 1. Run the Kivy app using terminal: 22 | 23 | - Activate Virtual Environment. 24 | 25 |
venv\scripts\activate
26 | 27 | - Run the App. 28 | 29 |
(venv) python main.py
30 | 31 | 2. Simply execute the `main.py` file from your editor. 32 | 33 | You should see the app window open with some basic app design. Make changes to the `.kv` files and save it to see the updates in real-time. 34 | 35 | Happy Coding! 36 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/HomeScreen/__init__.py: -------------------------------------------------------------------------------- 1 | from .home_screen import HomeScreenView 2 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/HomeScreen/home_screen.kv: -------------------------------------------------------------------------------- 1 | : 2 | MDNavigationLayout: 3 | MDScreenManager: 4 | MDScreen: 5 | MDStackLayout: 6 | size_hint: 1, 1 7 | padding: 3 8 | 9 | MDBoxLayout: 10 | adaptive_height: True 11 | spacing: 7 12 | padding: [5, 5, 5, 5] 13 | 14 | CustomIconButton: 15 | icon: "menu" 16 | on_press: 17 | nav_drawer.set_state("open") 18 | 19 | MDLabel: 20 | text: "KvDeveloper" 21 | font_style: "Title" 22 | role: "medium" 23 | valign: "center" 24 | theme_text_color: "Custom" 25 | text_color: app.theme_cls.onPrimaryContainerColor 26 | 27 | MDBoxLayout: 28 | adaptive_width: True 29 | MDAnchorLayout: 30 | anchor_x: 'right' 31 | MDGridLayout: 32 | rows: 1 33 | adaptive_size: True 34 | spacing: 5 35 | 36 | CustomIconButton: 37 | icon: "login" 38 | on_press: 39 | app.referrer("login screen") 40 | 41 | CustomIconButton: 42 | icon: 43 | "weather-night" if app.theme_cls.theme_style == 'Light' else "weather-sunny" 44 | on_press: 45 | app.apply_styles("Dark") if app.theme_cls.theme_style == 'Light' else app.apply_styles("Light") 46 | 47 | MDBoxLayout: 48 | orientation: "vertical" 49 | ScrollView: 50 | always_overscroll: False 51 | HomeScreenContext: 52 | orientation: 'vertical' 53 | padding: ["7dp", "7dp", "7dp", "7dp"] 54 | adaptive_height: True 55 | 56 | Widget: 57 | size_hint: 1, None 58 | 59 | MDNavigationDrawer: 60 | id: nav_drawer 61 | radius: [0, 15, 15, 0] 62 | orientation: "vertical" 63 | md_bg_color: app.theme_cls.surfaceContainerColor 64 | 65 | ContentNavigationDrawer: 66 | spacing: 10 67 | 68 | MDBoxLayout: 69 | adaptive_height: True 70 | MDAnchorLayout: 71 | anchor_y: 'bottom' 72 | anchor_x: "left" 73 | 74 | MDButton: 75 | style: "tonal" 76 | 77 | MDButtonIcon: 78 | icon: "copyright" 79 | 80 | MDButtonText: 81 | text: "Novfensec Inc." 82 | 83 | : 84 | icon: "blank" 85 | style: "tonal" 86 | 87 | : 88 | MDBoxLayout: 89 | orientation: "vertical" 90 | adaptive_height: True 91 | padding: ["7dp","7dp","7dp","7dp"] 92 | spacing: "7dp" 93 | radius: [25] 94 | md_bg_color: app.theme_cls.surfaceContainerLowColor 95 | 96 | MDBoxLayout: 97 | adaptive_height: True 98 | padding: ["7dp", "7dp", "7dp", "7dp"] 99 | 100 | MDBoxLayout: 101 | adaptive_height: True 102 | orientation: "vertical" 103 | MDLabel: 104 | text: "KvDeveloper" 105 | font_style: "Headline" 106 | role: "medium" 107 | theme_text_color: "Custom" 108 | text_color: app.theme_cls.onPrimaryContainerColor 109 | 110 | MDBoxLayout: 111 | orientation: 'vertical' 112 | adaptive_size: True 113 | MDAnchorLayout: 114 | anchor_x: 'right' 115 | anchor_y: 'top' 116 | size_hint_y: None 117 | height: avatar.height 118 | 119 | FitImage: 120 | id: avatar 121 | source: f"{app.image_library_path}/kvdeveloper/kvdeveloper_logo128.png" 122 | size_hint: None, None 123 | size: "95sp", "95sp" 124 | MDDivider: 125 | MDBoxLayout: 126 | orientation: 'vertical' 127 | adaptive_height: True 128 | spacing: "11dp" 129 | padding: ["7dp", "7dp", "7dp", "7dp"] 130 | 131 | MDLabel: 132 | multiline: True 133 | theme_font_size: "Custom" 134 | font_size: "16sp" 135 | adaptive_height: True 136 | text: 137 | "KvDeveloper is a PyPI module designed to streamline the development of Kivy and KivyMD applications. Inspired by Expo CLI for React Native, it offers starter templates and essential functionalities to kickstart your projects with ease. With features like predefined templates, MVC architecture support, and customizable options, KvDeveloper simplifies creating robust and organized Kivy projects." 138 | 139 | MDDivider: 140 | MDGridLayout: 141 | cols: 2 142 | adaptive_height: True 143 | padding: ["7dp", "7dp", "7dp", "7dp"] 144 | spacing: "15dp" 145 | 146 | MDAnchorLayout: 147 | md_bg_color: app.theme_cls.surfaceContainerColor 148 | radius: [15] 149 | size_hint_y: None 150 | 151 | MDIconButton: 152 | icon: "youtube" 153 | theme_icon_color: "Custom" 154 | icon_color: "red" 155 | theme_font_size: "Custom" 156 | size_hint: 1, 1 157 | font_size: "75sp" 158 | on_press: 159 | app.web_open("https://youtube.com/@NovfensecInc") 160 | 161 | MDAnchorLayout: 162 | md_bg_color: app.theme_cls.surfaceContainerColor 163 | radius: [15] 164 | size_hint_y: None 165 | 166 | MDIconButton: 167 | icon: "github" 168 | theme_icon_color: "Custom" 169 | icon_color: app.theme_cls.onSurfaceColor 170 | theme_font_size: "Custom" 171 | size_hint: 1, 1 172 | font_size: "75sp" 173 | on_press: 174 | app.web_open("https://github.com/Novfensec/KvDeveloper") 175 | 176 | MDAnchorLayout: 177 | md_bg_color: app.theme_cls.surfaceContainerColor 178 | radius: [15] 179 | size_hint_y: None 180 | 181 | MDIconButton: 182 | icon: "blank" 183 | theme_font_size: "Custom" 184 | size_hint: 1, 1 185 | font_size: "75sp" 186 | on_press: 187 | app.web_open("https://discord.com/invite/U9bfkD6A4c") 188 | 189 | FitImage: 190 | source: f"{app.image_library_path}/basic/discord128.png" 191 | size_hint: None, None 192 | size: "85dp", "85dp" 193 | 194 | MDAnchorLayout: 195 | md_bg_color: app.theme_cls.surfaceContainerColor 196 | radius: [15] 197 | size_hint_y: None 198 | 199 | MDIconButton: 200 | icon: "blank" 201 | theme_font_size: "Custom" 202 | size_hint: 1, 1 203 | font_size: "75sp" 204 | on_press: 205 | app.web_open("https://stackoverflow.com/users/16486510/novfensec") 206 | 207 | FitImage: 208 | source: f"{app.image_library_path}/basic/stack_overflow128.png" 209 | size_hint: None, None 210 | size: "75dp", "75dp" 211 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/HomeScreen/home_screen.py: -------------------------------------------------------------------------------- 1 | from View.base_screen import BaseScreenView 2 | 3 | 4 | class HomeScreenView(BaseScreenView): 5 | pass 6 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/LoginScreen/__init__.py: -------------------------------------------------------------------------------- 1 | from .login_screen import LoginScreenView 2 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/LoginScreen/login_screen.kv: -------------------------------------------------------------------------------- 1 | : 2 | MDScreenManager: 3 | id: login_manager_screens 4 | LoginSubScreen: 5 | manager_screens: login_manager_screens 6 | SignupSubScreen: 7 | manager_screens: login_manager_screens 8 | 9 | MDStackLayout: 10 | size_hint: 1, 1 11 | MDBoxLayout: 12 | adaptive_height: True 13 | padding: [5, 5, 5, 5] 14 | CustomIconButton: 15 | icon: 'arrow-left' 16 | on_press: 17 | app.referrer("home screen") 18 | 19 | : 20 | manager_screens: None 21 | name: "login sub screen" 22 | 23 | MDBoxLayout: 24 | orientation: 'vertical' 25 | MDScrollView: 26 | always_overscroll: False 27 | 28 | MDGridLayout: 29 | cols: 1 30 | adaptive_height: True 31 | padding: ['9dp', '9dp', '9dp', '45dp'] 32 | 33 | MDWidget: 34 | size_hint: 1, None 35 | height: "45dp" 36 | 37 | LoginBoxLayout: 38 | orientation: 'vertical' 39 | adaptive_height: True 40 | spacing: 7 41 | manager_screens: root.manager_screens 42 | 43 | : 44 | MDLabel: 45 | text: 'Login' 46 | font_style: "Display" 47 | role: "small" 48 | color: app.theme_cls.primaryColor 49 | halign: 'center' 50 | adaptive_height: True 51 | 52 | MDBoxLayout: 53 | orientation: 'vertical' 54 | adaptive_height:True 55 | padding: [5, 10, 5, 10] 56 | MDAnchorLayout: 57 | anchor_x: 'center' 58 | size_hint: 1, None 59 | height: avatar.height 60 | 61 | FitImage: 62 | id: avatar 63 | source: f"{app.image_library_path}/basic/login_illustration400.png" 64 | size_hint: None, None 65 | size: "200dp","145dp" 66 | 67 | MDBoxLayout: 68 | orientation: 'vertical' 69 | adaptive_height: True 70 | spacing: '16dp' 71 | md_bg_color: app.theme_cls.surfaceContainerLowColor 72 | radius: [19] 73 | padding: ['9dp', '14dp', '9dp', '14dp'] 74 | 75 | CustomTextField: 76 | icon_left: "account" 77 | hint_text: "Username" 78 | 79 | CustomTextField: 80 | icon_left: "lock" 81 | hint_text: "Password" 82 | 83 | MDTextFieldTrailingIcon: 84 | icon: "eye" 85 | theme_icon_color: "Custom" 86 | icon_color_normal: app.theme_cls.primaryColor 87 | icon_color_focus: app.theme_cls.primaryColor 88 | 89 | MDButton: 90 | style: "tonal" 91 | theme_width: "Custom" 92 | height: "56dp" 93 | size_hint_x: 1 94 | 95 | MDButtonText: 96 | text: "Login" 97 | font_style: "Title" 98 | role: "medium" 99 | pos_hint: {'center_x': 0.5, 'center_y': 0.5} 100 | 101 | MDButton: 102 | style: "text" 103 | pos_hint: {'center_x': 0.5} 104 | on_press: 105 | root.manager_screens.current="signup sub screen" 106 | MDButtonText: 107 | text: "Or Create an Account" 108 | 109 | Widget: 110 | MDBoxLayout: 111 | adaptive_height: True 112 | padding: ['9dp', '7dp', '9dp', '1dp'] 113 | 114 | MDDivider: 115 | 116 | MDLabel: 117 | id: centered_text 118 | adaptive_width: True 119 | text: " or Login with" 120 | 121 | MDDivider: 122 | 123 | MDAnchorLayout: 124 | anchor_x: "center" 125 | radius: [15] 126 | size_hint_y: None 127 | 128 | MDGridLayout: 129 | adaptive_size: True 130 | padding: ["7dp", "7dp", "7dp", "7dp"] 131 | cols: 2 132 | 133 | MDAnchorLayout: 134 | size_hint: None, None 135 | MDIconButton: 136 | icon: "blank" 137 | size_hint: 1, 1 138 | theme_font_size: "Custom" 139 | font_size: "51sp" 140 | 141 | FitImage: 142 | source: f"{app.image_library_path}/basic/google64.png" 143 | size_hint: None, None 144 | size: "45dp", "45dp" 145 | 146 | MDAnchorLayout: 147 | size_hint: None, None 148 | MDIconButton: 149 | icon: "github" 150 | size_hint: 1, 1 151 | theme_icon_color: "Custom" 152 | icon_color: app.theme_cls.onSurfaceColor 153 | theme_font_size: "Custom" 154 | font_size: "51sp" 155 | 156 | : 157 | manager_screens: None 158 | name: "signup sub screen" 159 | 160 | MDBoxLayout: 161 | orientation: 'vertical' 162 | MDScrollView: 163 | always_overscroll: False 164 | 165 | MDGridLayout: 166 | cols: 1 167 | adaptive_height: True 168 | padding: ['9dp', '9dp', '9dp', '45dp'] 169 | 170 | MDWidget: 171 | size_hint: 1, None 172 | height: "45dp" 173 | 174 | SignupBoxLayout: 175 | orientation: 'vertical' 176 | adaptive_height: True 177 | spacing: 7 178 | manager_screens: root.manager_screens 179 | 180 | : 181 | MDLabel: 182 | text: 'Signup' 183 | font_style: "Display" 184 | role: "small" 185 | color: app.theme_cls.primaryColor 186 | halign: 'center' 187 | 188 | MDBoxLayout: 189 | orientation: 'vertical' 190 | adaptive_height: True 191 | padding: [5, 10, 5, 10] 192 | MDAnchorLayout: 193 | anchor_x: 'center' 194 | size_hint: 1, None 195 | height: avatar.height 196 | 197 | FitImage: 198 | id: avatar 199 | source: f"{app.image_library_path}/basic/signup_illustration.png" 200 | size_hint: None, None 201 | size: "145dp","145dp" 202 | 203 | MDBoxLayout: 204 | orientation: 'vertical' 205 | adaptive_height: True 206 | md_bg_color: app.theme_cls.surfaceContainerLowColor 207 | spacing: '11dp' 208 | radius: [19] 209 | padding: ['9dp', '14dp', '9dp', '14dp'] 210 | 211 | CustomTextField: 212 | icon_left: "account" 213 | hint_text: "Username" 214 | 215 | CustomTextField: 216 | icon_left: "email" 217 | hint_text: "Email Address" 218 | email: True 219 | 220 | CustomTextField: 221 | icon_left: "lock" 222 | hint_text: "Password" 223 | MDTextFieldTrailingIcon: 224 | icon: "eye" 225 | theme_icon_color: "Custom" 226 | icon_color_normal: app.theme_cls.primaryColor 227 | icon_color_focus: app.theme_cls.primaryColor 228 | 229 | CustomTextField: 230 | icon_left: "lock" 231 | hint_text: "Confirm Password" 232 | MDTextFieldTrailingIcon: 233 | icon: "eye" 234 | theme_icon_color: "Custom" 235 | icon_color_normal: app.theme_cls.primaryColor 236 | icon_color_focus: app.theme_cls.primaryColor 237 | 238 | MDButton: 239 | style: "tonal" 240 | theme_width: "Custom" 241 | height: "56dp" 242 | size_hint_x: 1 243 | 244 | MDButtonText: 245 | text: "Signup" 246 | font_style: "Title" 247 | role: "medium" 248 | pos_hint: {'center_x': 0.5, 'center_y': 0.5} 249 | 250 | MDButton: 251 | style: "text" 252 | pos_hint: {'center_x': 0.5} 253 | on_press: 254 | root.manager_screens.current = "login sub screen" 255 | MDButtonText: 256 | text: "Or Login to your Account" 257 | 258 | Widget: 259 | MDBoxLayout: 260 | adaptive_height: True 261 | padding: ['9dp', '7dp', '9dp', '1dp'] 262 | 263 | MDDivider: 264 | 265 | MDLabel: 266 | id: centered_text 267 | adaptive_width: True 268 | text: " or Signup with" 269 | 270 | MDDivider: 271 | 272 | MDAnchorLayout: 273 | anchor_x: "center" 274 | radius: [15] 275 | size_hint_y: None 276 | 277 | MDGridLayout: 278 | adaptive_size: True 279 | padding: ["7dp", "7dp", "7dp", "7dp"] 280 | cols: 2 281 | 282 | MDAnchorLayout: 283 | size_hint: None, None 284 | MDIconButton: 285 | icon: "blank" 286 | size_hint: 1, 1 287 | theme_font_size: "Custom" 288 | font_size: "51sp" 289 | 290 | FitImage: 291 | source: f"{app.image_library_path}/basic/google64.png" 292 | size_hint: None, None 293 | size: "45dp", "45dp" 294 | 295 | MDAnchorLayout: 296 | size_hint: None, None 297 | MDIconButton: 298 | icon: "github" 299 | size_hint: 1, 1 300 | theme_icon_color: "Custom" 301 | icon_color: app.theme_cls.onSurfaceColor 302 | theme_font_size: "Custom" 303 | font_size: "51sp" 304 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/LoginScreen/login_screen.py: -------------------------------------------------------------------------------- 1 | from View.base_screen import BaseScreenView 2 | 3 | 4 | class LoginScreenView(BaseScreenView): 5 | pass 6 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/base_screen.kv: -------------------------------------------------------------------------------- 1 | #: import DrawerClickableItem View.extensions.DrawerClickableItem 2 | #: import CustomTextField View.extensions.CustomTextField 3 | 4 | : 5 | md_bg_color: app.theme_cls.backgroundColor 6 | 7 | : 8 | md_bg_color: app.theme_cls.transparentColor 9 | ripple_color: app.theme_cls.surfaceContainerHighColor 10 | inactive_indicator_color: app.theme_cls.transparentColor 11 | active_indicator_color: app.theme_cls.surfaceContainerHighColor 12 | 13 | MDNavigationDrawerItemLeadingIcon: 14 | icon: root.icon 15 | 16 | MDNavigationDrawerItemText: 17 | text: root.text 18 | 19 | : 20 | MDNavigationDrawerHeader: 21 | orientation:"vertical" 22 | adaptive_height: True 23 | 24 | MDLabel: 25 | text: "KvDeveloper" 26 | theme_text_color: "Custom" 27 | theme_line_height: "Custom" 28 | line_height: 0 29 | text_color: app.theme_cls.onPrimaryContainerColor 30 | adaptive_height: True 31 | font_style: "Display" 32 | role: "small" 33 | 34 | MDNavigationDrawerDivider: 35 | MDBoxLayout: 36 | adaptive_height: True 37 | orientation: "vertical" 38 | padding: ['9dp', '9dp', '9dp', '9dp'] 39 | spacing: '11dp' 40 | 41 | FitImage: 42 | id: avatar 43 | source: f"{app.image_library_path}/kvdeveloper/kvdeveloper_logo128.png" 44 | size_hint: None, None 45 | size: "100sp", "100sp" 46 | Widget: 47 | 48 | MDLabel: 49 | text: 'Novfensec Inc.' 50 | color: app.theme_cls.primaryColor 51 | style: "Body" 52 | role: "large" 53 | 54 | Widget: 55 | 56 | MDNavigationDrawerDivider: 57 | 58 | DrawerClickableItem: 59 | icon: "source-pull" 60 | text: "Screen 1" 61 | selected: True 62 | 63 | DrawerClickableItem: 64 | icon: "source-branch" 65 | text: "Screen 2" 66 | 67 | DrawerClickableItem: 68 | icon: "source-repository-multiple" 69 | text: "Screen 3" 70 | 71 | : 72 | helper_text: "" 73 | hint_text: "" 74 | icon_left: "blank" 75 | mode: "filled" 76 | theme_bg_color: "Custom" 77 | fill_color_normal: app.theme_cls.transparentColor 78 | fill_color_focus: self.fill_color_normal 79 | theme_line_color: "Custom" 80 | line_color_normal: self.fill_color_normal 81 | line_color_focus: self.fill_color_normal 82 | radius: [10] 83 | 84 | MDTextFieldLeadingIcon: 85 | icon: root.icon_left 86 | theme_icon_color: "Custom" 87 | icon_color_normal: app.theme_cls.primaryColor 88 | icon_color_focus: app.theme_cls.primaryColor 89 | 90 | MDTextFieldHintText: 91 | text: root.hint_text 92 | theme_text_color: "Custom" 93 | text_color_normal: app.theme_cls.outlineColor 94 | 95 | MDTextFieldHelperText: 96 | text: root.helper_text 97 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/base_screen.py: -------------------------------------------------------------------------------- 1 | from kivy.properties import ObjectProperty 2 | from kivymd.app import MDApp 3 | from kivymd.uix.screen import MDScreen 4 | 5 | 6 | class BaseScreenView(MDScreen): 7 | 8 | manager_screens = ObjectProperty() 9 | """ 10 | Screen manager object - :class:`~kivymd.uix.screenmanager.MDScreenManager`. 11 | 12 | :attr:`manager_screens` is an :class:`~kivy.properties.ObjectProperty` 13 | and defaults to `None`. 14 | """ 15 | 16 | def __init__(self, *args, **kwargs): 17 | super().__init__(*args, **kwargs) 18 | # Often you need to get access to the application object from the view 19 | # class. You can do this using this attribute. 20 | self.app = MDApp.get_running_app() 21 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/extensions.py: -------------------------------------------------------------------------------- 1 | from kivymd.uix.navigationdrawer import MDNavigationDrawerItem 2 | from kivymd.uix.textfield import MDTextField 3 | from kivy.properties import StringProperty 4 | 5 | 6 | class DrawerClickableItem(MDNavigationDrawerItem): 7 | icon = StringProperty() 8 | text = StringProperty() 9 | 10 | 11 | class CustomTextField(MDTextField): 12 | icon_left = StringProperty() 13 | hint_text = StringProperty() 14 | helper_text = StringProperty() 15 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/View/screens.py: -------------------------------------------------------------------------------- 1 | from View.HomeScreen.home_screen import HomeScreenView 2 | from View.LoginScreen.login_screen import LoginScreenView 3 | 4 | screens = { 5 | "home screen": { 6 | "object": HomeScreenView, 7 | "module": "View.HomeScreenView", 8 | }, 9 | "login screen": { 10 | "object": LoginScreenView, 11 | "module": "View.LoginScreenView", 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import webbrowser 3 | from kivymd.app import MDApp 4 | from kivymd.uix.screenmanager import MDScreenManager 5 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 6 | from kvdeveloper.config import IMAGE_LIBRARY 7 | from kivymd.utils.set_bars_colors import set_bars_colors 8 | from kivy.core.window import Window 9 | from kivy.clock import Clock 10 | 11 | Clock.max_iteration=30 12 | 13 | 14 | def set_softinput(*args) -> None: 15 | Window.keyboard_anim_args = {"d": 0.2, "t": "in_out_expo"} 16 | Window.softinput_mode = "below_target" 17 | 18 | 19 | Window.on_restore(Clock.schedule_once(set_softinput, 0.1)) 20 | 21 | 22 | class UI(MDScreenManager): 23 | def __init__(self, *args, **kwargs): 24 | super(UI, self).__init__(*args, **kwargs) 25 | self.transition = SAT() 26 | 27 | 28 | class {{project_name}}(MDApp): 29 | def __init__(self, *args, **kwargs): 30 | super({{project_name}}, self).__init__(*args, **kwargs) 31 | self.load_all_kv_files(os.path.join(self.directory, "View")) 32 | self.theme_cls.primary_palette = "Midnightblue" 33 | self.image_library_path = IMAGE_LIBRARY 34 | self.manager_screens = UI() 35 | 36 | def build(self) -> UI: 37 | self.generate_application_screens() 38 | self.apply_styles() 39 | return self.manager_screens 40 | 41 | def generate_application_screens(self) -> None: 42 | # adds different screen widgets to the screen manager 43 | import View.screens 44 | 45 | screens = View.screens.screens 46 | 47 | for i, name_screen in enumerate(screens.keys()): 48 | view = screens[name_screen]["object"]() 49 | view.manager_screens = self.manager_screens 50 | view.name = name_screen 51 | self.manager_screens.add_widget(view) 52 | 53 | def apply_styles(self, style: str = "Light") -> None: 54 | self.theme_cls.theme_style = style 55 | Window.clearcolor = self.theme_cls.backgroundColor 56 | if style == "Light": 57 | style = "Dark" 58 | self.set_bars_colors(style) 59 | 60 | def set_bars_colors(self, style: str = "Light") -> None: 61 | set_bars_colors( 62 | self.theme_cls.primaryColor, # status bar color 63 | self.theme_cls.primaryColor, # navigation bar color 64 | style, # icons color of status bar 65 | ) 66 | 67 | def referrer(self, destination: str = None) -> None: 68 | if self.manager_screens.current != destination: 69 | self.manager_screens.current = destination 70 | 71 | def web_open(self, url: str) -> None: 72 | webbrowser.open_new_tab(url) 73 | 74 | 75 | if __name__ == "__main__": 76 | {{project_name}}().run() 77 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/mainh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | from kivy import Config 4 | from PIL import ImageGrab 5 | 6 | resolution = ImageGrab.grab().size 7 | 8 | # Change the values of the application window size as you need. 9 | Config.set("graphics", "height", "690") 10 | Config.set("graphics", "width", "317") 11 | 12 | from kivy.core.window import Window 13 | 14 | # Place the application window on the right side of the computer screen. 15 | Window.top = 30 16 | Window.left = resolution[0] - Window.width + 5 17 | 18 | import webbrowser 19 | from kivymd.tools.hotreload.app import MDApp 20 | from kivymd.uix.screenmanager import MDScreenManager 21 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 22 | from kvdeveloper.config import IMAGE_LIBRARY 23 | from kivy.clock import Clock 24 | 25 | Clock.max_iteration = 30 26 | 27 | 28 | class UI(MDScreenManager): 29 | def __init__(self, *args, **kwargs): 30 | super(UI, self).__init__(*args, **kwargs) 31 | self.transition = SAT() 32 | 33 | 34 | class {{project_name}}(MDApp): 35 | def __init__(self, *args, **kwargs): 36 | super({{project_name}}, self).__init__(*args, **kwargs) 37 | self.DEBUG = True 38 | self.KV_DIRS = [ 39 | os.path.join(os.getcwd(), "View"), 40 | ] 41 | self.theme_cls.primary_palette = "Midnightblue" 42 | self.image_library_path = IMAGE_LIBRARY 43 | 44 | def build_app(self) -> UI: 45 | self.apply_styles("Light") 46 | self.manager_screens = UI() 47 | self.generate_application_screens() 48 | return self.manager_screens 49 | 50 | def generate_application_screens(self) -> None: 51 | """ 52 | Adds different screen widgets to the screen manager. 53 | """ 54 | import View.screens 55 | 56 | importlib.reload(View.screens) 57 | screens = View.screens.screens 58 | 59 | for i, name_screen in enumerate(screens.keys()): 60 | view = screens[name_screen]["object"]() 61 | view.manager_screens = self.manager_screens 62 | view.name = name_screen 63 | self.manager_screens.add_widget(view) 64 | 65 | def apply_styles(self, style: str = "Light") -> None: 66 | self.theme_cls.theme_style = style 67 | Window.clearcolor = self.theme_cls.backgroundColor 68 | 69 | def referrer(self, destination: str = None) -> None: 70 | if self.manager_screens.current != destination: 71 | self.manager_screens.current = destination 72 | 73 | def web_open(self, url: str) -> None: 74 | webbrowser.open_new_tab(url) 75 | 76 | 77 | if __name__ == "__main__": 78 | {{project_name}}().run() 79 | -------------------------------------------------------------------------------- /kvdeveloper/templates/nav_toolbar/requirements.txt: -------------------------------------------------------------------------------- 1 | kivy>=2.0.0 2 | https://github.com/kivymd/KivyMD/archive/master.zip 3 | watchdog>=4.0.1 4 | pillow>=10.0.0 5 | kvdeveloper>={{kvdeveloper_version}} -------------------------------------------------------------------------------- /kvdeveloper/templates/p4a/build.tmpl.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:8.1.1' 9 | {%- gradle_classpath %} 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | jcenter() 17 | {%- for repo in args.gradle_repositories %} 18 | {{repo}} 19 | {%- endfor %} 20 | } 21 | } 22 | 23 | {% if is_library %} 24 | apply plugin: 'com.android.library' 25 | {% else %} 26 | apply plugin: 'com.android.application' 27 | {% endif %} 28 | 29 | // gradle_plugin is the same as gradle_classpath (i think) 30 | {%- gradle_plugin %} 31 | 32 | android { 33 | namespace '{{ args.package }}' 34 | compileSdkVersion {{ android_api }} 35 | buildToolsVersion '{{ build_tools_version }}' 36 | defaultConfig { 37 | minSdkVersion {{ args.min_sdk_version }} 38 | targetSdkVersion {{ android_api }} 39 | versionCode {{ args.numeric_version }} 40 | versionName '{{ args.version }}' 41 | manifestPlaceholders = {{ args.manifest_placeholders}} 42 | } 43 | 44 | packagingOptions { 45 | jniLibs { 46 | useLegacyPackaging = true 47 | } 48 | {% if debug_build -%} 49 | doNotStrip '**/*.so' 50 | {% else %} 51 | exclude 'lib/**/gdbserver' 52 | exclude 'lib/**/gdb.setup' 53 | {%- endif %} 54 | } 55 | 56 | 57 | {% if args.sign -%} 58 | signingConfigs { 59 | release { 60 | storeFile file(System.getenv("P4A_RELEASE_KEYSTORE")) 61 | keyAlias System.getenv("P4A_RELEASE_KEYALIAS") 62 | storePassword System.getenv("P4A_RELEASE_KEYSTORE_PASSWD") 63 | keyPassword System.getenv("P4A_RELEASE_KEYALIAS_PASSWD") 64 | } 65 | } 66 | 67 | {%- endif %} 68 | 69 | {% if args.packaging_options -%} 70 | packagingOptions { 71 | {%- for option in args.packaging_options %} 72 | {{option}} 73 | {%- endfor %} 74 | } 75 | {%- endif %} 76 | 77 | buildTypes { 78 | debug { 79 | } 80 | release { 81 | {% if args.sign -%} 82 | signingConfig signingConfigs.release 83 | {%- endif %} 84 | } 85 | } 86 | 87 | compileOptions { 88 | sourceCompatibility JavaVersion.VERSION_1_8 89 | targetCompatibility JavaVersion.VERSION_1_8 90 | {%- for option in args.compile_options %} 91 | {{option}} 92 | {%- endfor %} 93 | } 94 | 95 | sourceSets { 96 | main { 97 | jniLibs.srcDir 'libs' 98 | java { 99 | 100 | {%- for adir, pattern in args.extra_source_dirs -%} 101 | srcDir '{{adir}}' 102 | {%- endfor -%} 103 | 104 | } 105 | } 106 | } 107 | 108 | aaptOptions { 109 | noCompress "tflite" 110 | } 111 | 112 | } 113 | 114 | dependencies { 115 | {%- gradle_bom %} 116 | {%- gradle_dep %} 117 | 118 | {%- for aar in aars %} 119 | implementation(name: '{{ aar }}', ext: 'aar') 120 | {%- endfor -%} 121 | {%- for jar in jars %} 122 | implementation files('src/main/libs/{{ jar }}') 123 | {%- endfor -%} 124 | {%- if args.depends -%} 125 | {%- for depend in args.depends %} 126 | implementation '{{ depend }}' 127 | {%- endfor %} 128 | {%- endif %} 129 | {% if args.presplash_lottie %} 130 | implementation 'com.airbnb.android:lottie:6.1.0' 131 | {%- endif %} 132 | } 133 | -------------------------------------------------------------------------------- /kvdeveloper/templates/p4a/gradle.json: -------------------------------------------------------------------------------- 1 | { 2 | "classpaths": [], 3 | "plugins": [], 4 | "boms": [], 5 | "deps": [] 6 | } 7 | -------------------------------------------------------------------------------- /kvdeveloper/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | import tarfile 3 | from typing import Dict, Literal 4 | 5 | 6 | def replace_placeholders(content: str, variables: Dict[str, str]) -> str: 7 | """ 8 | Replace placeholders in the content with provided variables. 9 | 10 | :param content: The content with placeholders. 11 | :param variables: A dictionary of variables to replace in the content. 12 | :return: The content with replaced variables. 13 | """ 14 | for placeholder, value in variables.items(): 15 | content = content.replace(f"{{{{{placeholder}}}}}", value) 16 | return content 17 | 18 | 19 | def name_parser_snake(name: str) -> str: 20 | """ 21 | Convert a PascalCase name to snake_case. 22 | 23 | :param name: The name to be converted. 24 | :return: The converted snake_case name. 25 | """ 26 | # Convert name to lowercase and ensure 'screen' is separated with '_' 27 | snake_case = name.lower() 28 | snake_case = re.sub(r"_?screen", "_screen", snake_case) 29 | return snake_case 30 | 31 | 32 | def name_parser(name: str, parse_type: Literal["screen", "project"]) -> str: 33 | """ 34 | Parse the name according to the given parse type. 35 | 36 | :param name: The name to be parsed. 37 | :param parse_type: The type of parsing to perform ("screen" or "project"). 38 | :return: The parsed name in PascalCase. 39 | :raises ValueError: If the name is invalid. 40 | :raises TypeError: If the parse type is invalid. 41 | """ 42 | name = name.lower() 43 | 44 | if parse_type == "screen": 45 | # Validate and format screen name 46 | if name and name[0].isdigit(): 47 | raise ValueError("The name of the screen should not start with a number.") 48 | elif "screen" not in name: 49 | name += "Screen" 50 | elif name.lower() == "screen": 51 | raise ValueError( 52 | "The name of the screen cannot be only 'screen' or 'Screen'." 53 | ) 54 | 55 | # Extract and capitalize words 56 | words = re.split(r"[^a-zA-Z0-9]", name) 57 | pascal_case = "".join( 58 | word.capitalize() if word.lower() != "screen" else "Screen" 59 | for word in words 60 | if word 61 | ) 62 | pascal_case = re.sub(r"Screen", "Screen", pascal_case, flags=re.IGNORECASE) 63 | elif parse_type == "project": 64 | # Validate and format project name 65 | if name and name[0].isdigit(): 66 | raise ValueError("The name of the project should not start with a number.") 67 | elif "app" not in name: 68 | name += "App" 69 | elif name.lower() == "app": 70 | raise ValueError("The name of the project cannot be only 'app' or 'App'.") 71 | 72 | # Extract and capitalize words 73 | words = re.split(r"[^a-zA-Z0-9]", name) 74 | pascal_case = "".join( 75 | word.capitalize() if word.lower() != "app" else "App" 76 | for word in words 77 | if word 78 | ) 79 | pascal_case = re.sub(r"App", "App", pascal_case, flags=re.IGNORECASE) 80 | else: 81 | raise TypeError( 82 | f"Invalid parse type: '{parse_type}'. Should be one of ['screen', 'project']" 83 | ) 84 | 85 | return pascal_case 86 | 87 | 88 | def extract_tar_file(tar_file_path: str, extract_to_path: str) -> None: 89 | """ 90 | Extracts a .tar file to a specified directory. 91 | 92 | Args: 93 | tar_file_path (str): Path to the .tar file. 94 | extract_to_path (str): Directory to extract the files into. 95 | """ 96 | try: 97 | with tarfile.open(tar_file_path, "r") as tar: 98 | tar.extractall(path=extract_to_path, members=tar.getmembers()) 99 | print(f"Extracted '{tar_file_path}' to '{extract_to_path}' successfully.") 100 | except Exception as e: 101 | print(f"Error extracting file: {e}") 102 | -------------------------------------------------------------------------------- /kvdeveloper/view_base/buildozer.spec: -------------------------------------------------------------------------------- 1 | [app] 2 | 3 | # (str) Title of your application 4 | title = {{project_name}} 5 | 6 | # (str) Package name 7 | package.name = {{project_package_name}} 8 | 9 | # (str) Package domain (needed for android/ios packaging) 10 | package.domain = org.novfensec.{{project_package_name}} 11 | 12 | # (str) Source code where the main.py live 13 | source.dir = . 14 | 15 | # (list) Source files to include (let empty to include all the files) 16 | source.include_exts = py,png,jpg,kv,atlas 17 | 18 | # (list) List of inclusions using pattern matching 19 | source.include_patterns = assets/*,assets/images/*.png 20 | 21 | # (list) Source files to exclude (let empty to not exclude anything) 22 | #source.exclude_exts = spec 23 | 24 | # (list) List of directory to exclude (let empty to not exclude anything) 25 | #source.exclude_dirs = tests, bin, venv 26 | 27 | # (list) List of exclusions using pattern matching 28 | # Do not prefix with './' 29 | #source.exclude_patterns = license,images/*/*.jpg 30 | 31 | # (str) Application versioning (method 1) 32 | version = 0.1 33 | 34 | # (str) Application versioning (method 2) 35 | # version.regex = __version__ = ['"](.*)['"] 36 | # version.filename = %(source.dir)s/main.py 37 | 38 | # (list) Application requirements 39 | # comma separated e.g. requirements = sqlite3,kivy 40 | requirements = python3, kivy==2.3.0, https://github.com/kivymd/KivyMD/archive/master.zip, pillow, materialyoucolor, exceptiongroup, asyncgui, asynckivy 41 | 42 | # (str) Custom source folders for requirements 43 | # Sets custom source for any requirements with recipes 44 | # requirements.source.kivy = ../../kivy 45 | 46 | # (str) Presplash of the application 47 | #presplash.filename = %(source.dir)s/data/presplash.png 48 | 49 | # (str) Icon of the application 50 | #icon.filename = %(source.dir)s/data/icon.png 51 | 52 | # (list) Supported orientations 53 | # Valid options are: landscape, portrait, portrait-reverse or landscape-reverse 54 | orientation = portrait 55 | 56 | # (list) List of service to declare 57 | #services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY 58 | 59 | # 60 | # OSX Specific 61 | # 62 | 63 | # 64 | # author = © Copyright Info 65 | 66 | # change the major version of python used by the app 67 | osx.python_version = 3 68 | 69 | # Kivy version to use 70 | osx.kivy_version = 2.3.0 71 | 72 | # 73 | # Android specific 74 | # 75 | 76 | # (bool) Indicate if the application should be fullscreen or not 77 | fullscreen = 0 78 | 79 | # (string) Presplash background color (for android toolchain) 80 | # Supported formats are: #RRGGBB #AARRGGBB or one of the following names: 81 | # red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray, 82 | # darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy, 83 | # olive, purple, silver, teal. 84 | #android.presplash_color = #FFFFFF 85 | 86 | # (string) Presplash animation using Lottie format. 87 | # see https://lottiefiles.com/ for examples and https://airbnb.design/lottie/ 88 | # for general documentation. 89 | # Lottie files can be created using various tools, like Adobe After Effect or Synfig. 90 | #android.presplash_lottie = "path/to/lottie/file.json" 91 | 92 | # (str) Adaptive icon of the application (used if Android API level is 26+ at runtime) 93 | #icon.adaptive_foreground.filename = %(source.dir)s/data/icon_fg.png 94 | #icon.adaptive_background.filename = %(source.dir)s/data/icon_bg.png 95 | 96 | # (list) Permissions 97 | # (See https://python-for-android.readthedocs.io/en/latest/buildoptions/#build-options-1 for all the supported syntaxes and properties) 98 | #android.permissions = android.permission.INTERNET, (name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=18) 99 | 100 | # (list) features (adds uses-feature -tags to manifest) 101 | #android.features = android.hardware.usb.host 102 | 103 | # (int) Target Android API, should be as high as possible. 104 | #android.api = 35 105 | 106 | # (int) Minimum API your APK / AAB will support. 107 | #android.minapi = 21 108 | 109 | # (int) Android SDK version to use 110 | #android.sdk = 20 111 | 112 | # (str) Android NDK version to use 113 | #android.ndk = 25b 114 | 115 | # (int) Android NDK API to use. This is the minimum API your app will support, it should usually match android.minapi. 116 | #android.ndk_api = 21 117 | 118 | # (bool) Use --private data storage (True) or --dir public storage (False) 119 | #android.private_storage = True 120 | 121 | # (str) Android NDK directory (if empty, it will be automatically downloaded.) 122 | #android.ndk_path = 123 | 124 | # (str) Android SDK directory (if empty, it will be automatically downloaded.) 125 | #android.sdk_path = 126 | 127 | # (str) ANT directory (if empty, it will be automatically downloaded.) 128 | #android.ant_path = 129 | 130 | # (bool) If True, then skip trying to update the Android sdk 131 | # This can be useful to avoid excess Internet downloads or save time 132 | # when an update is due and you just want to test/build your package 133 | # android.skip_update = False 134 | 135 | # (bool) If True, then automatically accept SDK license 136 | # agreements. This is intended for automation only. If set to False, 137 | # the default, you will be shown the license when first running 138 | # buildozer. 139 | android.accept_sdk_license = True 140 | 141 | # (str) Android entry point, default is ok for Kivy-based app 142 | #android.entrypoint = org.kivy.android.PythonActivity 143 | 144 | # (str) Full name including package path of the Java class that implements Android Activity 145 | # use that parameter together with android.entrypoint to set custom Java class instead of PythonActivity 146 | #android.activity_class_name = org.kivy.android.PythonActivity 147 | 148 | # (str) Extra xml to write directly inside the element of AndroidManifest.xml 149 | # use that parameter to provide a filename from where to load your custom XML code 150 | #android.extra_manifest_xml = ./src/android/extra_manifest.xml 151 | 152 | # (str) Extra xml to write directly inside the tag of AndroidManifest.xml 153 | # use that parameter to provide a filename from where to load your custom XML arguments: 154 | #android.extra_manifest_application_arguments = ./src/android/extra_manifest_application_arguments.xml 155 | 156 | # (str) Full name including package path of the Java class that implements Python Service 157 | # use that parameter to set custom Java class which extends PythonService 158 | #android.service_class_name = org.kivy.android.PythonService 159 | 160 | # (str) Android app theme, default is ok for Kivy-based app 161 | # android.apptheme = "@android:style/Theme.NoTitleBar" 162 | 163 | # (list) Pattern to whitelist for the whole project 164 | #android.whitelist = 165 | 166 | # (str) Path to a custom whitelist file 167 | #android.whitelist_src = 168 | 169 | # (str) Path to a custom blacklist file 170 | #android.blacklist_src = 171 | 172 | # (list) List of Java .jar files to add to the libs so that pyjnius can access 173 | # their classes. Don't add jars that you do not need, since extra jars can slow 174 | # down the build process. Allows wildcards matching, for example: 175 | # OUYA-ODK/libs/*.jar 176 | #android.add_jars = foo.jar,bar.jar,path/to/more/*.jar 177 | 178 | # (list) List of Java files to add to the android project (can be java or a 179 | # directory containing the files) 180 | #android.add_src = 181 | 182 | # (list) Android AAR archives to add 183 | #android.add_aars = 184 | 185 | # (list) Put these files or directories in the apk assets directory. 186 | # Either form may be used, and assets need not be in 'source.include_exts'. 187 | # 1) android.add_assets = source_asset_relative_path 188 | # 2) android.add_assets = source_asset_path:destination_asset_relative_path 189 | #android.add_assets = 190 | 191 | # (list) Put these files or directories in the apk res directory. 192 | # The option may be used in three ways, the value may contain one or zero ':' 193 | # Some examples: 194 | # 1) A file to add to resources, legal resource names contain ['a-z','0-9','_'] 195 | # android.add_resources = my_icons/all-inclusive.png:drawable/all_inclusive.png 196 | # 2) A directory, here 'legal_icons' must contain resources of one kind 197 | # android.add_resources = legal_icons:drawable 198 | # 3) A directory, here 'legal_resources' must contain one or more directories, 199 | # each of a resource kind: drawable, xml, etc... 200 | # android.add_resources = legal_resources 201 | #android.add_resources = 202 | 203 | # (list) Gradle dependencies to add 204 | #android.gradle_dependencies = 205 | 206 | # (bool) Enable AndroidX support. Enable when 'android.gradle_dependencies' 207 | # contains an 'androidx' package, or any package from Kotlin source. 208 | # android.enable_androidx requires android.api >= 28 209 | #android.enable_androidx = True 210 | 211 | # (list) add java compile options 212 | # this can for example be necessary when importing certain java libraries using the 'android.gradle_dependencies' option 213 | # see https://developer.android.com/studio/write/java8-support for further information 214 | # android.add_compile_options = "sourceCompatibility = 1.8", "targetCompatibility = 1.8" 215 | 216 | # (list) Gradle repositories to add {can be necessary for some android.gradle_dependencies} 217 | # please enclose in double quotes 218 | # e.g. android.gradle_repositories = "maven { url 'https://kotlin.bintray.com/ktor' }" 219 | #android.add_gradle_repositories = 220 | 221 | # (list) packaging options to add 222 | # see https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html 223 | # can be necessary to solve conflicts in gradle_dependencies 224 | # please enclose in double quotes 225 | # e.g. android.add_packaging_options = "exclude 'META-INF/common.kotlin_module'", "exclude 'META-INF/*.kotlin_module'" 226 | #android.add_packaging_options = 227 | 228 | # (list) Java classes to add as activities to the manifest. 229 | #android.add_activities = com.example.ExampleActivity 230 | 231 | # (str) OUYA Console category. Should be one of GAME or APP 232 | # If you leave this blank, OUYA support will not be enabled 233 | #android.ouya.category = GAME 234 | 235 | # (str) Filename of OUYA Console icon. It must be a 732x412 png image. 236 | #android.ouya.icon.filename = %(source.dir)s/data/ouya_icon.png 237 | 238 | # (str) XML file to include as an intent filters in tag 239 | #android.manifest.intent_filters = 240 | 241 | # (list) Copy these files to src/main/res/xml/ (used for example with intent-filters) 242 | #android.res_xml = PATH_TO_FILE, 243 | 244 | # (str) launchMode to set for the main activity 245 | #android.manifest.launch_mode = standard 246 | 247 | # (str) screenOrientation to set for the main activity. 248 | # Valid values can be found at https://developer.android.com/guide/topics/manifest/activity-element 249 | #android.manifest.orientation = fullSensor 250 | 251 | # (list) Android additional libraries to copy into libs/armeabi 252 | #android.add_libs_armeabi = libs/android/*.so 253 | #android.add_libs_armeabi_v7a = libs/android-v7/*.so 254 | #android.add_libs_arm64_v8a = libs/android-v8/*.so 255 | #android.add_libs_x86 = libs/android-x86/*.so 256 | #android.add_libs_mips = libs/android-mips/*.so 257 | 258 | # (bool) Indicate whether the screen should stay on 259 | # Don't forget to add the WAKE_LOCK permission if you set this to True 260 | #android.wakelock = False 261 | 262 | # (list) Android application meta-data to set (key=value format) 263 | #android.meta_data = 264 | 265 | # (list) Android library project to add (will be added in the 266 | # project.properties automatically.) 267 | #android.library_references = 268 | 269 | # (list) Android shared libraries which will be added to AndroidManifest.xml using tag 270 | #android.uses_library = 271 | 272 | # (str) Android logcat filters to use 273 | #android.logcat_filters = *:S python:D 274 | 275 | # (bool) Android logcat only display log for activity's pid 276 | #android.logcat_pid_only = False 277 | 278 | # (str) Android additional adb arguments 279 | #android.adb_args = -H host.docker.internal 280 | 281 | # (bool) Copy library instead of making a libpymodules.so 282 | #android.copy_libs = 1 283 | 284 | # (list) The Android archs to build for, choices: armeabi-v7a, arm64-v8a, x86, x86_64 285 | # In past, was `android.arch` as we weren't supporting builds for multiple archs at the same time. 286 | android.archs = arm64-v8a, armeabi-v7a 287 | 288 | # (int) overrides automatic versionCode computation (used in build.gradle) 289 | # this is not the same as app version and should only be edited if you know what you're doing 290 | # android.numeric_version = 1 291 | 292 | # (bool) enables Android auto backup feature (Android API >=23) 293 | android.allow_backup = True 294 | 295 | # (str) XML file for custom backup rules (see official auto backup documentation) 296 | # android.backup_rules = 297 | 298 | # (str) If you need to insert variables into your AndroidManifest.xml file, 299 | # you can do so with the manifestPlaceholders property. 300 | # This property takes a map of key-value pairs. (via a string) 301 | # Usage example : android.manifest_placeholders = [myCustomUrl:\"org.kivy.customurl\"] 302 | # android.manifest_placeholders = [:] 303 | 304 | # (bool) Skip byte compile for .py files 305 | # android.no-byte-compile-python = False 306 | 307 | # (str) The format used to package the app for release mode (aab or apk or aar). 308 | # android.release_artifact = aab 309 | 310 | # (str) The format used to package the app for debug mode (apk or aar). 311 | # android.debug_artifact = apk 312 | 313 | # 314 | # Python for android (p4a) specific 315 | # 316 | 317 | # (str) python-for-android URL to use for checkout 318 | #p4a.url = 319 | 320 | # (str) python-for-android fork to use in case if p4a.url is not specified, defaults to upstream (kivy) 321 | #p4a.fork = kivy 322 | 323 | # (str) python-for-android branch to use, defaults to master 324 | #p4a.branch = master 325 | 326 | # (str) python-for-android specific commit to use, defaults to HEAD, must be within p4a.branch 327 | #p4a.commit = HEAD 328 | 329 | # (str) python-for-android git clone directory (if empty, it will be automatically cloned from github) 330 | #p4a.source_dir = 331 | 332 | # (str) The directory in which python-for-android should look for your own build recipes (if any) 333 | #p4a.local_recipes = 334 | 335 | # (str) Filename to the hook for p4a 336 | #p4a.hook = 337 | 338 | # (str) Bootstrap to use for android builds 339 | # p4a.bootstrap = sdl2 340 | 341 | # (int) port number to specify an explicit --port= p4a argument (eg for bootstrap flask) 342 | #p4a.port = 343 | 344 | # Control passing the --use-setup-py vs --ignore-setup-py to p4a 345 | # "in the future" --use-setup-py is going to be the default behaviour in p4a, right now it is not 346 | # Setting this to false will pass --ignore-setup-py, true will pass --use-setup-py 347 | # NOTE: this is general setuptools integration, having pyproject.toml is enough, no need to generate 348 | # setup.py if you're using Poetry, but you need to add "toml" to source.include_exts. 349 | #p4a.setup_py = false 350 | 351 | # (str) extra command line arguments to pass when invoking pythonforandroid.toolchain 352 | #p4a.extra_args = 353 | 354 | 355 | 356 | # 357 | # iOS specific 358 | # 359 | 360 | # (str) Path to a custom kivy-ios folder 361 | #ios.kivy_ios_dir = ../kivy-ios 362 | # Alternately, specify the URL and branch of a git checkout: 363 | ios.kivy_ios_url = https://github.com/kivy/kivy-ios 364 | # ios.kivy_ios_url = https://github.com/Novfensec/kivy-ios 365 | ios.kivy_ios_branch = master 366 | # ios.kivy_ios_branch = patch-2 367 | 368 | # Another platform dependency: ios-deploy 369 | # Uncomment to use a custom checkout 370 | #ios.ios_deploy_dir = ../ios_deploy 371 | # Or specify URL and branch 372 | ios.ios_deploy_url = https://github.com/ios-control/ios-deploy 373 | ios.ios_deploy_branch = master 374 | 375 | # (bool) Whether or not to sign the code 376 | ios.codesign.allowed = false 377 | 378 | # (str) Name of the certificate to use for signing the debug version 379 | # Get a list of available identities: buildozer ios list_identities 380 | #ios.codesign.debug = "iPhone Developer: ()" 381 | 382 | # (str) The development team to use for signing the debug version 383 | #ios.codesign.development_team.debug = 384 | 385 | # (str) Name of the certificate to use for signing the release version 386 | #ios.codesign.release = %(ios.codesign.debug)s 387 | 388 | # (str) The development team to use for signing the release version 389 | #ios.codesign.development_team.release = 390 | 391 | # (str) URL pointing to .ipa file to be installed 392 | # This option should be defined along with `display_image_url` and `full_size_image_url` options. 393 | #ios.manifest.app_url = 394 | 395 | # (str) URL pointing to an icon (57x57px) to be displayed during download 396 | # This option should be defined along with `app_url` and `full_size_image_url` options. 397 | #ios.manifest.display_image_url = 398 | 399 | # (str) URL pointing to a large icon (512x512px) to be used by iTunes 400 | # This option should be defined along with `app_url` and `display_image_url` options. 401 | #ios.manifest.full_size_image_url = 402 | 403 | 404 | [buildozer] 405 | 406 | # (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) 407 | log_level = 2 408 | 409 | # (int) Display warning if buildozer is run as root (0 = False, 1 = True) 410 | warn_on_root = 1 411 | 412 | # (str) Path to build artifact storage, absolute or relative to spec file 413 | # build_dir = ./.buildozer 414 | 415 | # (str) Path to build output (i.e. .apk, .aab, .ipa) storage 416 | # bin_dir = ./bin 417 | 418 | # ----------------------------------------------------------------------------- 419 | # List as sections 420 | # 421 | # You can define all the "list" as [section:key]. 422 | # Each line will be considered as a option to the list. 423 | # Let's take [app] / source.exclude_patterns. 424 | # Instead of doing: 425 | # 426 | #[app] 427 | #source.exclude_patterns = license,data/audio/*.wav,data/images/original/* 428 | # 429 | # This can be translated into: 430 | # 431 | #[app:source.exclude_patterns] 432 | #license 433 | #data/audio/*.wav 434 | #data/images/original/* 435 | # 436 | 437 | 438 | # ----------------------------------------------------------------------------- 439 | # Profiles 440 | # 441 | # You can extend section / key with a profile 442 | # For example, you want to deploy a demo version of your application without 443 | # HD content. You could first change the title to add "(demo)" in the name 444 | # and extend the excluded directories to remove the HD content. 445 | # 446 | #[app@demo] 447 | #title = My Application (demo) 448 | # 449 | #[app:source.exclude_patterns@demo] 450 | #images/hd/* 451 | # 452 | # Then, invoke the command line with the "demo" profile: 453 | # 454 | #buildozer --profile demo android debug 455 | -------------------------------------------------------------------------------- /kvdeveloper/view_base/default_component.py: -------------------------------------------------------------------------------- 1 | from kivymd.uix.widget import Widget 2 | 3 | 4 | class {{component_name}}(Widget): 5 | def __init__(self, *args, **kwargs): 6 | super({{component_name}}, self).__init__(*args, **kwargs) 7 | -------------------------------------------------------------------------------- /kvdeveloper/view_base/default_screen.kv: -------------------------------------------------------------------------------- 1 | <{{parsed_name}}View>: 2 | MDStackLayout: 3 | size_hint: 1, 1 4 | padding: [20, 20, 20, 20] 5 | spacing: 15 6 | 7 | MDBoxLayout: 8 | adaptive_height: True 9 | spacing: 15 10 | 11 | MDIconButton: 12 | icon: "arrow-left" 13 | style: "tonal" 14 | color_map: "secodary" 15 | theme_bg_color: "Custom" 16 | md_bg_color: [0.5, 0.6, 1, 0.1] 17 | -------------------------------------------------------------------------------- /kvdeveloper/view_base/default_screen.py: -------------------------------------------------------------------------------- 1 | from View.base_screen import BaseScreenView 2 | 3 | 4 | class {{parsed_name}}View(BaseScreenView): 5 | def __init__(self, *args, **kwargs): 6 | super({{parsed_name}}View, self).__init__(*args, **kwargs) -------------------------------------------------------------------------------- /kvdeveloper/view_base/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Script for managing hot reloading of the project. 3 | For more details see the documentation page - 4 | 5 | https://kivymd.readthedocs.io/en/latest/api/kivymd/tools/patterns/create_project/ 6 | 7 | To run the application in hot boot mode, execute the command in the console: 8 | set DEBUG=1 && python main.py 9 | """ 10 | 11 | import importlib 12 | import os 13 | 14 | from kivy import Config 15 | 16 | # Change the values of the application window size as you need. 17 | Config.set("graphics", "height", "715") 18 | Config.set("graphics", "width", "317") 19 | 20 | # TODO: You may know an easier way to get the size of a computer display. 21 | from PIL import ImageGrab 22 | from kivy.core.window import Window 23 | 24 | resolution = ImageGrab.grab().size 25 | 26 | # Place the application window on the right side of the computer screen. 27 | Window.top = 30 28 | Window.left = resolution[0] - Window.width + 5 29 | 30 | import webbrowser 31 | from kivymd.tools.hotreload.app import MDApp 32 | from kivymd.uix.screenmanager import MDScreenManager 33 | from kivymd.uix.transition import MDSharedAxisTransition as SAT 34 | from kvdeveloper.config import IMAGE_LIBRARY 35 | from kivy.clock import Clock 36 | 37 | Clock.max_iteration = 30 38 | 39 | 40 | class UI(MDScreenManager): 41 | def __init__(self, *args, **kwargs): 42 | super(UI, self).__init__(*args, **kwargs) 43 | self.transition = SAT() 44 | 45 | 46 | class {{project_name}}(MDApp): 47 | DEBUG = True 48 | KV_DIRS = [os.path.join(os.getcwd(), "View")] 49 | 50 | def __init__(self, **kwargs): 51 | super().__init__(**kwargs) 52 | self.theme_cls.primary_palette = "Midnightblue" 53 | self.image_library_path = IMAGE_LIBRARY 54 | self.apply_styles("Light") 55 | 56 | def build_app(self) -> UI: 57 | """ 58 | In this method, you don't need to change anything other than the 59 | application theme. 60 | """ 61 | 62 | import View.screens 63 | 64 | self.manager_screens = UI() 65 | Window.bind(on_key_down=self.on_keyboard_down) 66 | importlib.reload(View.screens) 67 | screens = View.screens.screens 68 | 69 | for i, name_screen in enumerate(screens.keys()): 70 | model = screens[name_screen]["model"]() 71 | controller = screens[name_screen]["controller"](model) 72 | view = controller.get_view() 73 | view.manager_screens = self.manager_screens 74 | view.name = name_screen 75 | self.manager_screens.add_widget(view) 76 | 77 | return self.manager_screens 78 | 79 | def apply_styles(self, style: str = "Light") -> None: 80 | self.theme_cls.theme_style = style 81 | Window.clearcolor = self.theme_cls.backgroundColor 82 | 83 | def referrer(self, destination: str = None) -> None: 84 | if self.manager_screens.current != destination: 85 | self.manager_screens.current = destination 86 | 87 | def web_open(self, url: str) -> None: 88 | webbrowser.open_new_tab(url) 89 | 90 | def on_keyboard_down(self, window, keyboard, keycode, text, modifiers) -> None: 91 | """ 92 | The method handles keyboard events. 93 | 94 | By default, a forced restart of an application is tied to the 95 | `CTRL+R` key on Windows OS and `COMMAND+R` on Mac OS. 96 | """ 97 | 98 | if "meta" in modifiers or "ctrl" in modifiers and text == "r": 99 | self.rebuild() 100 | 101 | 102 | if __name__ == "__main__": 103 | {{project_name}}().run() 104 | 105 | # After you finish the project, remove the above code and uncomment the below 106 | # code to test the application normally without hot reloading. 107 | 108 | # """ 109 | # The entry point to the application. 110 | # 111 | # The application uses the MVC template. Adhering to the principles of clean 112 | # architecture means ensuring that your application is easy to test, maintain, 113 | # and modernize. 114 | # 115 | # You can read more about this template at the links below: 116 | # 117 | # https://github.com/HeaTTheatR/LoginAppMVC 118 | # https://en.wikipedia.org/wiki/Model–view–controller 119 | # """ 120 | 121 | # import os 122 | # import webbrowser 123 | # from kivymd.app import MDApp 124 | # from kivymd.uix.screenmanager import MDScreenManager 125 | # from kivymd.uix.transition import MDSharedAxisTransition as SAT 126 | # from kivymd.utils.set_bars_colors import set_bars_colors 127 | # from kvdeveloper.config import IMAGE_LIBRARY 128 | # from kivy.clock import Clock 129 | # from kivy.core.window import Window 130 | # from View.screens import screens 131 | 132 | # Clock.max_iteration = 30 133 | 134 | 135 | # def set_softinput(*args) -> None: 136 | # Window.keyboard_anim_args = {"d": 0.2, "t": "in_out_expo"} 137 | # Window.softinput_mode = "below_target" 138 | 139 | 140 | # Window.on_restore(Clock.schedule_once(set_softinput, 0.1)) 141 | 142 | 143 | # class UI(MDScreenManager): 144 | # def __init__(self, *args, **kwargs): 145 | # super(UI, self).__init__(*args, **kwargs) 146 | # self.transition = SAT() 147 | 148 | 149 | # class {{project_name}}(MDApp): 150 | # def __init__(self, **kwargs): 151 | # super().__init__(**kwargs) 152 | # self.load_all_kv_files(os.path.join(self.directory, "View")) 153 | # self.theme_cls.primary_palette = "Midnightblue" 154 | # self.image_library_path = IMAGE_LIBRARY 155 | # # This is the screen manager that will contain all the screens of your application. 156 | # self.manager_screens = UI() 157 | 158 | # def build(self) -> UI: 159 | # self.generate_application_screens() 160 | # self.apply_styles("Light") 161 | # return self.manager_screens 162 | 163 | # def generate_application_screens(self) -> None: 164 | # """ 165 | # Creating and adding screens to the screen manager. 166 | # You should not change this cycle unnecessarily. He is self-sufficient. 167 | 168 | # If you need to add any screen, open the `View.screens.py` module and 169 | # see how new screens are added according to the given application 170 | # architecture. 171 | # """ 172 | 173 | # for i, name_screen in enumerate(screens.keys()): 174 | # model = screens[name_screen]["model"]() 175 | # controller = screens[name_screen]["controller"](model) 176 | # view = controller.get_view() 177 | # view.manager_screens = self.manager_screens 178 | # view.name = name_screen 179 | # self.manager_screens.add_widget(view) 180 | 181 | # def apply_styles(self, style: str = "Light") -> None: 182 | # self.theme_cls.theme_style = style 183 | # Window.clearcolor = self.theme_cls.backgroundColor 184 | # if style == "Light": 185 | # style = "Dark" 186 | # self.set_bars_colors(style) 187 | 188 | # def set_bars_colors(self, style: str = "Light") -> None: 189 | # set_bars_colors( 190 | # self.theme_cls.primaryColor, # status bar color 191 | # self.theme_cls.primaryColor, # navigation bar color 192 | # style, # icons color of status bar 193 | # ) 194 | 195 | # def referrer(self, destination: str = None) -> None: 196 | # if self.manager_screens.current != destination: 197 | # self.manager_screens.current = destination 198 | 199 | # def web_open(self, url: str) -> None: 200 | # webbrowser.open_new_tab(url) 201 | 202 | 203 | # if __name__ == "__main__": 204 | # {{project_name}}().run() 205 | -------------------------------------------------------------------------------- /kvdeveloper/view_base/registers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | from typing import List, Optional 4 | from kivy.factory import Factory 5 | from kivy.core.text import LabelBase 6 | 7 | # Alias for the register function from Factory 8 | component_register = Factory.register 9 | 10 | # Get the absolute path to the "View" directory 11 | view_path = os.path.join(os.getcwd(), "View") 12 | 13 | # Pattern to match all "components" directories recursively within "View" 14 | component_dir_pattern = os.path.join(view_path, "**", "components") 15 | 16 | # Find all directories that match the component pattern 17 | component_dirs: List[str] = glob.glob(component_dir_pattern, recursive=True) 18 | 19 | """ 20 | Registers custom components to the Kivy Factory. 21 | 22 | Below code searches for all directories named "components" within the "View" directory and registers each component to the Kivy Factory. 23 | Once registered, the components can be used without explicitly importing them elsewhere in the kvlang files. 24 | """ 25 | 26 | for component_path in component_dirs: 27 | # Loop through all items in the current component directory 28 | for component in os.listdir(component_path): 29 | target_dir = os.path.join(component_path, component) 30 | 31 | # Skip "__pycache__" and check if the item is a valid directory 32 | if component != "__pycache__" and os.path.isdir(target_dir): 33 | # Get the relative path to the target directory 34 | rel_path = os.path.relpath(target_dir) 35 | 36 | # Extract the module name (last part of the relative path) 37 | module_name = os.path.basename(rel_path) 38 | 39 | # Convert the path to a Python importable module format 40 | module_import_path = rel_path.replace(os.sep, ".") 41 | 42 | # Register the component with Kivy's Factory 43 | component_register(module_name, module=module_import_path) 44 | 45 | 46 | """ 47 | Registers custom fonts to the Kivy LabelBase. 48 | 49 | Below code searches for all directories within the "assets/fonts" directory and registers each font to the Kivy LabelBase. 50 | Once registered, the fonts can be used without explicitly importing them elsewhere in the kvlang files. 51 | 52 | To successfully register the fonts, the directory structure should contain font files for regular, bold, italic, and bolditalic styles, as shown below: 53 | 54 | - Example Directory Structure: 55 | 56 | TestApp 57 | └── assets 58 | └── fonts 59 | └── TestFont 60 | ├── testfont-bold.ttf 61 | ├── testfont-bolditalic.ttf 62 | ├── testfont-italic.ttf 63 | └── testfont-regular.ttf 64 | 65 | - Usage in `kvlang` files: 66 | 67 | ``` 68 | MDLabel: 69 | text: "TestFont" 70 | font_name: "TestFont" # (Case Sensitive Name) 71 | bold: True 72 | italic: True 73 | ``` 74 | 75 | """ 76 | 77 | # Alias for the register function from LabelBase 78 | font_register = LabelBase.register 79 | 80 | # Get the absolute path to the "assets/fonts" directory 81 | font_dir = os.path.join(os.getcwd(), "assets", "fonts") 82 | 83 | 84 | def get_font_path(directory: str, font_name: str, style: str) -> Optional[str]: 85 | """ 86 | Helper function to construct and check the path for a specific font style. 87 | 88 | Args: 89 | directory (str): The path to the font's directory. 90 | font_name (str): The base name of the font. 91 | style (str): The style of the font (e.g., regular, italic, bold, bolditalic). 92 | 93 | Returns: 94 | Optional[str]: The path to the font file if it exists, otherwise None. 95 | """ 96 | font_path = os.path.join(directory, f"{font_name.lower()}-{style}.ttf") 97 | return font_path if os.path.isfile(font_path) else None 98 | 99 | 100 | # Iterate over the fonts in the directory 101 | for font_name in os.listdir(font_dir): 102 | target_dir = os.path.join(font_dir, font_name) 103 | 104 | # Check if it's a directory (excluding '__pycache__') 105 | if os.path.isdir(target_dir) and font_name != "__pycache__": 106 | 107 | # Fetch paths for different font styles if available 108 | regular_font = get_font_path(target_dir, font_name, "regular") 109 | italic_font = get_font_path(target_dir, font_name, "italic") 110 | bold_font = get_font_path(target_dir, font_name, "bold") 111 | bolditalic_font = get_font_path(target_dir, font_name, "bolditalic") 112 | 113 | # Register the font with the LabelBase 114 | font_register(font_name, regular_font, italic_font, bold_font, bolditalic_font) 115 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "kvdeveloper" 7 | dynamic = ["version"] 8 | dependencies = [ 9 | "kivy>=2.0.0", 10 | "kivymd>=2.0.0", 11 | "pillow>=10.0.0", 12 | "typer>=0.12.3", 13 | "rich>=13.7.1", 14 | "requests>=2.32.0" 15 | ] 16 | requires-python = ">=3.9" 17 | authors = [ 18 | {name = "Kartavya Shukla", email = "novfensec@protonmail.com"}, 19 | ] 20 | maintainers = [ 21 | {name = "Kartavya Shukla", email = "novfensec@protonmail.com"}, 22 | ] 23 | description = "KvDeveloper is a PyPI module designed to streamline the development of Kivy and KivyMD applications. Inspired by Expo CLI for React Native, KvDeveloper provides starter templates and essential functionalities to kickstart your projects with ease." 24 | readme = "README.md" 25 | license = {file = "LICENSE"} 26 | keywords = ["kivy", "kivymd", "open source", "kvdeveloper", "kartavya shukla", "novfensec", "kv"] 27 | classifiers = [ 28 | "Development Status :: 4 - Beta", 29 | "Intended Audience :: Developers", 30 | "Topic :: Software Development :: Build Tools", 31 | "License :: OSI Approved :: MIT License", 32 | "Programming Language :: Python :: 3" 33 | ] 34 | 35 | [project.urls] 36 | Homepage = "https://github.com/Novfensec/KvDeveloper" 37 | Documentation = "https://novfensec.github.io/KvDeveloper.docs" 38 | Repository = "https://github.com/Novfensec/KvDeveloper" 39 | "Bug Tracker" = "https://github.com/Novfensec/KvDeveloper/issues" 40 | Changelog = "https://github.com/Novfensec/KvDeveloper/blob/main/CHANGELOG.md" 41 | 42 | [tool.setuptools.dynamic] 43 | version = {attr = "kvdeveloper.__version__"} 44 | 45 | [project.scripts] 46 | kvdeveloper = "kvdeveloper.__main__:main" 47 | kvd = "kvdeveloper.__main__:main" 48 | 49 | [project.optional-dependencies] 50 | all = [ 51 | "markdown2", 52 | "pyqt5", 53 | "pyqtwebengine", 54 | ] 55 | 56 | [tool.setuptools] 57 | package-dir = {"kvdeveloper" = "kvdeveloper"} 58 | 59 | [tool.setuptools.package-data] 60 | "kvdeveloper" = [ 61 | "**/*.md", 62 | "**/*.txt", 63 | "**/*.ipynb", 64 | "**/*.kv", 65 | "**/*.yml", 66 | "**/*.png", 67 | "**/*.spec", 68 | "**/*.js", 69 | "**/*.css", 70 | "templates/p4a/build.tmpl.gradle", 71 | "templates/p4a/gradle.json", 72 | ] 73 | 74 | [tool.setuptools.exclude-package-data] 75 | "kvdeveloper" = ["*.pyc", "*.pyo"] 76 | --------------------------------------------------------------------------------