Updated kivymd

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff