├── .gitignore
├── .idea
├── .gitignore
├── inspectionProfiles
│ └── profiles_settings.xml
├── kivyEmu.iml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── assets
├── android_lolipop1.png
├── kivy-icon-128.png
├── kivyemu.PNG
├── kivyemu.gif
├── kivyemu2.PNG
└── pyjokes.png
├── emulator.py
├── history.json
├── kaki
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-36.pyc
│ └── app.cpython-36.pyc
└── app.py
├── kivyEmu.spec
├── kivyemu.kv
├── kivymd
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __init__.py
├── accordion.py
├── accordionlistitem.py
├── backgroundcolorbehavior.py
├── bottomnavigation.py
├── bottomsheet.py
├── button.py
├── card.py
├── cards.py
├── chips.py
├── color_definitions.py
├── date_picker.py
├── dialog.py
├── elevation.py
├── elevationbehavior.py
├── fanscreenmanager.py
├── filemanager.py
├── font_definitions.py
├── fonts
│ ├── Roboto-Black.ttf
│ ├── Roboto-BlackItalic.ttf
│ ├── Roboto-Bold.ttf
│ ├── Roboto-BoldItalic.ttf
│ ├── Roboto-Italic.ttf
│ ├── Roboto-Light.ttf
│ ├── Roboto-LightItalic.ttf
│ ├── Roboto-Medium.ttf
│ ├── Roboto-MediumItalic.ttf
│ ├── Roboto-Regular.ttf
│ ├── Roboto-Thin.ttf
│ ├── Roboto-ThinItalic.ttf
│ └── materialdesignicons-webfont.ttf
├── grid.py
├── icon_definitions.py
├── imagelists.py
├── images
│ ├── dialog_in_fade.png
│ ├── folder.png
│ ├── ios_bg_mod.png
│ ├── ios_bg_mod_for_toast.png
│ ├── ios_entr_ti.png
│ ├── kivy-logo-white-512.png
│ ├── kivymd_512.png
│ ├── kivymd_logo.png
│ ├── quad_shadow-0.png
│ ├── quad_shadow-1.png
│ ├── quad_shadow-2.png
│ ├── quad_shadow.atlas
│ ├── rec_shadow-0.png
│ ├── rec_shadow-1.png
│ ├── rec_shadow.atlas
│ ├── rec_st_shadow-0.png
│ ├── rec_st_shadow-1.png
│ ├── rec_st_shadow-2.png
│ ├── rec_st_shadow.atlas
│ ├── round_shadow-0.png
│ ├── round_shadow-1.png
│ ├── round_shadow-2.png
│ ├── round_shadow.atlas
│ ├── swipe_shadow.png
│ └── transparent.png
├── label.py
├── list.py
├── managerswiper.py
├── material_resources.py
├── menu.py
├── menus.py
├── navigationdrawer.py
├── pickers.py
├── popupscreen.py
├── progressbar.py
├── progressloader.py
├── refreshlayout.py
├── ripplebehavior.py
├── selectioncontrols.py
├── setup.py
├── slider.py
├── slidingpanel.py
├── snackbar.py
├── snackbars.py
├── spinner.py
├── stackfloatingbuttons.py
├── stiffscroll
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
├── tabs.py
├── textfields.py
├── theme_picker.py
├── theming.py
├── theming_dynamic_text.py
├── time_picker.py
├── toast
│ ├── LICENSE
│ ├── README.md
│ ├── __init__.py
│ ├── androidtoast
│ │ ├── __init__.py
│ │ └── androidtoast.py
│ └── kivytoast
│ │ ├── __init__.py
│ │ └── kivytoast.py
├── toolbar.py
├── useranimationcard.py
├── utils
│ ├── __init__.py
│ ├── asynckivy.py
│ └── cropimage.py
└── vendor
│ ├── __init__.py
│ ├── circleLayout
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
│ ├── circularTimePicker
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
│ └── navigationdrawer
│ ├── LICENSE
│ ├── README.md
│ └── __init__.py
├── main.py
├── test.kv
└── test.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | *.pyc
3 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/kivyEmu.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | KivyLiteEmulator
24 |
25 |
26 | An Lite Emulator for Kivy Hot Reloading
27 |
28 | Demo Video »
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | ## About The Project
43 |
44 | Kivy is an awesome framework for creating GUI apps. However it lacks one of the most sought after features of a GUI framework which is an
45 | autoreloader. We are currently working on a kivystudio. Before releasing a stable version, i just need something to help me display my UI in realtime.
46 |
47 | This currently works with some of the apps i have created, it seems it is scared of bigger projects,:smile: lol,
48 |
49 | You may also suggest changes by forking this repo and creating a pull request or opening an issue.
50 |
51 | A list of commonly used resources that I find helpful are listed in the acknowledgements.
52 |
53 |
54 |
55 |
56 |
57 | ## Getting Started
58 |
59 | This is an example of how you may give instructions on setting up your project locally.
60 | To get a local copy up and running follow these simple example steps.
61 |
62 | ### Prerequisites
63 |
64 | This is an example of how to list things you need to use the software and how to install them. I Hope you already have `kivy` installed
65 |
66 | ```sh
67 | pip install monotonic watchdog plyer
68 | ```
69 |
70 | ### Installation
71 |
72 |
73 | 1. Clone the repo
74 | ```sh
75 | git clone https:://github.com/mcroni/KivyLiteEmulator.git
76 | ```
77 | 3. Change Dir into the Cloned Repo
78 | ```sh
79 | cd KivyLiteEmulator
80 | ```
81 | 3. Set `DEBUG=True` in the `OS` Environ
82 | ```sh
83 | set DEBUG = True
84 | ```
85 | 4. Run the `main.py` file and select a python file containing your kivy codes to run
86 | ```sh
87 | python main.py
88 | ```
89 |
90 |
91 |
92 |
93 |
94 |
95 | ## Contributing
96 |
97 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
98 |
99 | 1. Fork the Project
100 | 2. Create your Feature Branch
101 | 3. Commit your Changes
102 | 4. Push to the Branch
103 | 5. Open a Pull Request
104 |
105 |
106 |
107 |
108 | ## License
109 |
110 | Distributed under the MIT License. See `LICENSE` for more information.
111 |
112 |
113 |
114 |
115 | ## Contact
116 | [@kojo_mcroni](https://twitter.com/kojo_mcroni) - joeydanieldarko@gmail.com
117 |
118 |
119 |
120 |
121 | ## Acknowledgements
122 | * [Kaki By Tito](https://github.com/tito/kaki)
123 | * [KivyStudio](https://github.com/avour/kivystudio)
124 | * [KivyMD](https://github.com/HeaTTheatR/KivyMD)
125 | * [Kivy](https://kivy.org)
126 | * [Cruor99](https://github.com/cruor99)
127 |
128 |
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/assets/android_lolipop1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/assets/android_lolipop1.png
--------------------------------------------------------------------------------
/assets/kivy-icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/assets/kivy-icon-128.png
--------------------------------------------------------------------------------
/assets/kivyemu.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/assets/kivyemu.PNG
--------------------------------------------------------------------------------
/assets/kivyemu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/assets/kivyemu.gif
--------------------------------------------------------------------------------
/assets/kivyemu2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/assets/kivyemu2.PNG
--------------------------------------------------------------------------------
/assets/pyjokes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/assets/pyjokes.png
--------------------------------------------------------------------------------
/emulator.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 | import traceback
3 | from threading import Thread
4 | from functools import partial
5 | try:
6 | from importlib import reload
7 | except:
8 | pass
9 | from kivy.lang import Builder
10 | from kivy.clock import mainthread
11 | from kivy.resources import resource_add_path, resource_remove_path
12 | # from kivystudio.components.emulator_area import emulator_area
13 | from kivy.uix.floatlayout import FloatLayout
14 | from kivy.uix.boxlayout import BoxLayout
15 | from kivy.uix.label import Label
16 | from kivy.uix.behaviors import ToggleButtonBehavior
17 | from kivy.uix.screenmanager import ScreenManager, Screen
18 | from kivy.lang import Builder
19 | from kivy.properties import ObjectProperty, StringProperty
20 | from kivy.clock import Clock
21 | import os
22 | # __all__ = ('get_emulator',)
23 | # filepath = os.path.dirname(__file__)
24 | # Builder.load_file(os.path.join(filepath, 'emulator.kv'))
25 |
26 |
27 | # class EmulatorArea(BoxLayout):
28 | # screen_display = ObjectProperty(None)
29 | # emulation_file = StringProperty('')
30 | #
31 | # def __init__(self, **kwargs):
32 | # super(EmulatorArea, self).__init__(**kwargs)
33 | # self.screen_manager = EmulatorScreens()
34 | # self.add_widget(self.screen_manager)
35 | # # self.screen_display = ScreenDisplay()
36 | # self.screen_manager.add_widget(self.screen_display)
37 | #
38 | # def add_widget(self, widget):
39 | # super(EmulatorArea, self).add_widget(widget)
40 |
41 | # def toggle_orientation(self):
42 | # if self.screen_display.screen.orientation == 'portrait':
43 | # self.screen_display.screen.orientation = 'landscape'
44 | # else:
45 | # self.screen_display.screen.orientation = 'portrait'
46 |
47 | # def open_screen_drop(self, widget):
48 | # ScreenDrop().open(widget, self.screen_display)
49 |
50 |
51 |
52 |
53 |
54 | #
55 | # instance = []
56 | #
57 | #
58 | # def emulator_area():
59 | # if instance:
60 | # return instance[0]
61 | # else:
62 | # emulator_area = EmulatorArea(size_hint_x=.45)
63 | # instance.append(emulator_area)
64 | # return emulator_area
65 |
66 |
67 | def emulate_file(filename, threaded=False):
68 | root = None
69 | if not os.path.exists(filename):
70 | return
71 |
72 | dirname = os.path.dirname(filename)
73 | sys.path.append(dirname)
74 | os.chdir(dirname)
75 | resource_add_path(dirname)
76 |
77 | emulator_area().screen_display.screen.clear_widgets()
78 |
79 | if threaded:
80 | Thread(target=partial(start_emulation, filename, threaded=threaded)).start()
81 | else:
82 | start_emulation(filename, threaded=threaded)
83 |
84 |
85 | def start_emulation(filename, threaded=False):
86 | root = None
87 | if os.path.splitext(filename)[1] == '.kv': # load the kivy file directly
88 | try: # cahching error with kivy files
89 | Builder.unload_file(filename)
90 | root = Builder.load_file(filename)
91 | except:
92 | traceback.print_exc()
93 | print("You kivy file has a problem")
94 |
95 | elif os.path.splitext(filename)[1] == '.py':
96 | load_defualt_kv(filename)
97 |
98 | try: # cahching error with python files
99 | root = load_py_file(filename)
100 | except:
101 | traceback.print_exc()
102 | print("You python file has a problem")
103 |
104 | if root:
105 | if threaded:
106 | emulation_done(root, filename)
107 | else:
108 | emulator_area().screen_display.screen.add_widget(root)
109 |
110 | dirname = os.path.dirname(filename)
111 | sys.path.pop()
112 | resource_remove_path(dirname)
113 |
114 |
115 | @mainthread
116 | def emulation_done(root, filename):
117 | if root:
118 | emulator_area().screen_display.screen.add_widget(root)
119 |
120 |
121 | def load_defualt_kv(filename):
122 | app_cls_name = get_app_cls_name(filename)
123 | if app_cls_name is None:
124 | return
125 |
126 | kv_name = app_cls_name.lower()
127 | if app_cls_name.endswith('App'):
128 | kv_name = app_cls_name[:len(app_cls_name) - 3].lower()
129 |
130 | if app_cls_name:
131 | file_dir = os.path.dirname(filename)
132 | kv_filename = os.path.join(file_dir, kv_name + '.kv')
133 |
134 | if os.path.exists(kv_filename):
135 | try: # cahching error with kivy files
136 | Builder.unload_file(kv_filename)
137 | root = Builder.load_file(kv_filename)
138 | except:
139 | traceback.print_exc()
140 | print("You kivy file has a problem")
141 |
142 |
143 | def get_app_cls_name(filename):
144 | with open(filename) as fn:
145 | text = fn.read()
146 |
147 | lines = text.splitlines()
148 | app_cls = get_import_as('from kivy.app import App', lines)
149 |
150 | def check_app_cls(line):
151 | line = line.strip()
152 | return line.startswith('class') and line.endswith('(%s):' % app_cls)
153 |
154 | found = list(filter(check_app_cls, lines))
155 |
156 | if found:
157 | line = found[0]
158 | cls_name = line.split('(')[0].split(' ')[1]
159 | return cls_name
160 |
161 |
162 | def get_root_from_runTouch():
163 | with open(filename) as fn:
164 | text = fn.read()
165 |
166 | lines = text.splitlines()
167 | run_touch = get_import_as('from kivy.base import runTouchApp', lines)
168 |
169 | def check_run_touch(line):
170 | line = line.strip()
171 | return line.startswith('%s(' % run_touch)
172 |
173 | found = list(filter(check_run_touch, lines))
174 |
175 | if found:
176 | line = found[0]
177 | root_name = line.strip().split('(')[1].split(')')[0]
178 |
179 | root_file = import_from_dir(filename)
180 | root = getattr(reload(root_file), root_name)
181 |
182 | return root
183 |
184 |
185 | def load_py_file(filename):
186 | app_cls_name = get_app_cls_name(filename)
187 | if app_cls_name:
188 | root_file = import_from_dir(filename)
189 | app_cls = getattr(reload(root_file), app_cls_name)
190 | root = app_cls().build()
191 |
192 | return root
193 |
194 | run_root = get_root_from_runTouch(filename)
195 | if run_root:
196 | return run_root
197 |
198 |
199 | def import_from_dir(filename):
200 | ''' force python to import this file
201 | from the project_ dir'''
202 |
203 | dirname, file = os.path.split(filename)
204 | sys.path = [dirname] + sys.path
205 |
206 | import_word = os.path.splitext(file)[0]
207 | imported = __import__(import_word)
208 | return imported
209 |
210 |
211 | def get_import_as(start, lines):
212 | line = list(filter(lambda line: line.strip().startswith(start), lines))
213 | if line:
214 | words = line[0].split(' ')
215 | import_word = words[len(words) - 1]
216 | return import_word
217 | else:
218 | return
219 |
220 |
--------------------------------------------------------------------------------
/history.json:
--------------------------------------------------------------------------------
1 | {"C:\\Users\\MCRONI\\PycharmProjects\\kivyemu_test\\main.py": {"file_name": "C:\\Users\\MCRONI\\PycharmProjects\\kivyemu_test\\main.py"}}
--------------------------------------------------------------------------------
/kaki/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | __version__ = "0.1.4.dev0"
4 |
--------------------------------------------------------------------------------
/kaki/__pycache__/__init__.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kaki/__pycache__/__init__.cpython-36.pyc
--------------------------------------------------------------------------------
/kaki/__pycache__/app.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kaki/__pycache__/app.cpython-36.pyc
--------------------------------------------------------------------------------
/kivyEmu.spec:
--------------------------------------------------------------------------------
1 | # -*- mode: python -*-
2 | from kivy_deps import sdl2, glew
3 |
4 | block_cipher = None
5 |
6 |
7 | a = Analysis(['main.py'],
8 | pathex=['C:\\Users\\MCRONI\\PycharmProjects\\kivyEmu'],
9 | binaries=[],
10 | datas=[],
11 | hiddenimports=['kivymd.theming', 'kivymd', 'kivymd.selectioncontrols', 'kivymd.card', 'kivymd.tabs', 'kivymd.menu','plyer','plyer.platforms','plyer.platforms.win','plyer.platforms.win.filechooser'],
12 | hookspath=[],
13 | runtime_hooks=[],
14 | excludes=[],
15 | win_no_prefer_redirects=False,
16 | win_private_assemblies=False,
17 | cipher=block_cipher)
18 | pyz = PYZ(a.pure, a.zipped_data,
19 | cipher=block_cipher)
20 | exe = EXE(pyz,
21 | a.scripts,
22 | exclude_binaries=True,
23 | name='kivyEmi',
24 | debug=False,
25 | strip=False,
26 | upx=True,
27 | console=True )
28 |
29 | coll = COLLECT(exe, Tree('.'),
30 | a.binaries,
31 | a.zipfiles,
32 | a.datas,
33 | *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)],
34 | strip=False,
35 | upx=True,
36 | name='kivyemu')
37 |
--------------------------------------------------------------------------------
/kivyemu.kv:
--------------------------------------------------------------------------------
1 | #:import OneLineIconListItem kivymd.list.OneLineIconListItem
2 | #:import images_path kivymd.images_path
3 | #:import MDTextFieldRect kivymd.textfields.MDTextField
4 | #:import MDIconButton kivymd.button.MDIconButton
5 | #:import MDDropdownMenu kivymd.menus.MDDropdownMenu
6 | #:import MDList kivymd.list.MDList
7 | #:import OneLineListItem kivymd.list.OneLineListItem
8 | #:import TwoLineListItem kivymd.list.TwoLineListItem
9 | #:import ThreeLineListItem kivymd.list.ThreeLineListItem
10 | #:import OneLineAvatarListItem kivymd.list.OneLineAvatarListItem
11 | #:import OneLineIconListItem kivymd.list.OneLineIconListItem
12 | #:import OneLineAvatarIconListItem kivymd.list.OneLineAvatarIconListItem
13 | #:import MDTabsBase kivymd.tabs.MDTabsBase
14 | #:import MDTabs kivymd.tabs.MDTabs
15 | #:import MDRaisedButton kivymd.button.MDRaisedButton
16 | #:import MDTextFieldRect kivymd.textfields.MDTextFieldRect
17 | #:import MDTextFieldClear kivymd.textfields.MDTextFieldClear
18 | #:import MDTextField kivymd.textfields.MDTextField
19 | #:import MDTextFieldRound kivymd.textfields.MDTextFieldRound
20 | #:import MDRaisedButton kivymd.button.MDRaisedButton
21 | #:import MDLabel kivymd.label.MDLabel
22 | #:import MDToolbar kivymd.toolbar.MDToolbar
23 | #:import NavigationDrawerToolbar kivymd.navigationdrawer.NavigationDrawerToolbar
24 | #:import NavigationLayout kivymd.navigationdrawer.NavigationLayout
25 | #:import MDSpinner kivymd.spinner.MDSpinner
26 | #: import get_color_from_hex kivy.utils.get_color_from_hex
27 |
28 | :
29 | screen_manager:screen_manager
30 | emulator_screen:emulator_screen
31 | Image:
32 | source: './assets/android_lolipop1.png'
33 | pos_hint: {'center_x': 0.5, 'center_y': 0.46}
34 |
35 | BoxLayout:
36 | orientation: "vertical"
37 | MDToolbar:
38 | id: toolbar
39 | title: 'KivyEmu'
40 | md_bg_color: app.theme_cls.primary_color
41 | background_palette: 'Primary'
42 | background_hue: '500'
43 | elevation: 10
44 | right_action_items:
45 | [['menu', lambda x:app.show_example_bottom_sheet()]]
46 | BoxLayout:
47 | id: box_man
48 | FloatLayout
49 | BoxLayout:
50 | size_hint: .926,.8325
51 | pos_hint: {'center_x': 0.5, 'center_y': 0.525}
52 | ScreenManager:
53 | id:screen_manager
54 | EmulatorScreen:
55 | id: emulator_screen
56 | name: "emulator_screen"
57 | HistoryScreen:
58 | id: history_screen
59 | name: "history_screen"
60 | # MDFloatingActionButton:
61 | # id:chevron_up
62 | # icon: 'file'
63 | # opacity: 0.9
64 | # pos_hint: {'center_x': 0.8, 'center_y': 0.1}
65 | # size_hint: None, None
66 | # size: dp(55), dp(55)
67 | # on_press:app.choose()
68 |
69 |
70 |
71 | :
72 | on_pre_enter: root.build_screen()
73 |
--------------------------------------------------------------------------------
/kivymd/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.swp
3 | .buildozer
4 | .idea
5 | build
6 | docs/_build
7 | demos/kitchen_sink/bin
--------------------------------------------------------------------------------
/kivymd/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 |
4 | ## [0.99.92]
5 |
6 | - Removed automatic change of text field length in the `MDTextFieldRound` class
7 |
8 | ## [0.99.93]
9 |
10 | - Updated `materialdesignicons-webfont.ttf` and `icon_definitions.py` files
11 |
12 | ## [0.99.94]
13 |
14 | - Added property `_no_ripple_effect` to BaseListItem class
15 | - [Banned](https://www.youtube.com/watch?v=P_9oSx0Pz_U) use `ripple effect` in MDAccordionListItem class
16 | - Added check to use `ripple effect` in RectangularRippleBehavior class
17 |
18 | ## [0.99.95]
19 |
20 | - Added function to create a round image in `kivymd/utils/cropimage.py` module
21 | - Added new `MDCustomRoundIconButton` class in `kivymd/button.py` module
22 | - Added demo application [Account Page](https://www.youtube.com/watch?v=dfUOwqtYoYg)
23 |
24 | ## [0.99.96]
25 | - Added asynchronous call to list update method in RefreshLayout example in main.py file
26 | - Added asynckivy.py module for using asynchronous function calls in Kivy
27 |
28 | ## [0.99.97]
29 | - Fixed: spinner closes after updating the screen.
30 |
31 | ## [0.99.98]
32 | - Added new MDFillRoundFlatIconButton class
33 |
34 | ## [0.100.0]
35 | - Fix "In MDNavigationDrawer I used use_logo='all' it's showing image only not the drawer_title text!"
--------------------------------------------------------------------------------
/kivymd/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors - KivyMD library up to version 0.1.2
4 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors - KivyMD library version 0.1.3 and higher
5 |
6 | Other libraries used in the project:
7 |
8 | Copyright (c) 2010-2019 Kivy Team and other contributors
9 | Copyright (c) 2013 Brian Knapp - Androidoast library
10 | Copyright (c) 2013 Alexander Taylor - navigationdrawer library
11 | Copyright (c) 2014 LogicalDash - stiffscroll library
12 | Copyright (c) 2015 Davide Depau - circularTimePicker, circleLayout libraries
13 | Copyright (c) 2015 Kivy Garden - tabs module
14 | Copyright (c) 2019 Nattōsai Mitō - asynckivy module
15 |
16 | Permission is hereby granted, free of charge, to any person obtaining a copy
17 | of this software and associated documentation files (the "Software"), to deal
18 | in the Software without restriction, including without limitation the rights
19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 | copies of the Software, and to permit persons to whom the Software is
21 | furnished to do so, subject to the following conditions:
22 |
23 | The above copyright notice and this permission notice shall be included in
24 | all copies or substantial portions of the Software.
25 |
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32 | THE SOFTWARE.
33 |
--------------------------------------------------------------------------------
/kivymd/README.md:
--------------------------------------------------------------------------------
1 | KivyMD
2 | ======
3 |
4 |
5 |
6 | KivyMD is a collection of Material Design compliant widgets for use with [Kivy](http://kivy.org), a framework for cross-platform, touch-enabled graphical applications.
7 |
8 | The project's goal is to approximate Google's [Material Design spec](https://www.google.com/design/spec/material-design/introduction.html) as close as possible without sacrificing ease of use or application performance.
9 |
10 | This library is a fork of the [KivyMD project](https://gitlab.com/kivymd/KivyMD) the author of which stopped supporting this project three years ago. We found the strength and brought this project to a new level.
11 |
12 | Currently we're in **alpha** status, so things are changing all the time and we cannot promise any kind of API stability. However it is safe to vendor now and make use of what's currently available.
13 |
14 | Join the project! Just fork the project, branch out and submit a pull request when your patch is ready. If any changes are necessary, we'll guide you through the steps that need to be done via PR comments or access to your for may be requested to outright submit them.
15 |
16 | If you wish to become a project developer (permission to create branches on the project without forking for easier collaboration), have at least one PR approved and ask for it. If you contribute regularly to the project the role may be offered to you without asking too.
17 |
18 | Documentation
19 | =============
20 |
21 | Full documentation yet. Some examples of using KivyMD widgets can be found in our [Wiki](https://github.com/HeaTTheatR/KivyMD/wiki) and in the code of the [demo applications](https://github.com/HeaTTheatR/KivyMD/tree/master/demos/kitchen_sink/demo_apps)
22 |
23 | Support
24 | =======
25 | If you need assistance, you can ask for help on our mailing list:
26 |
27 | * User Groups: [vk group](https://vk.com/kivy_development), [google group](https://groups.google.com/forum/#!categories/kivymd-users-support), [Discord Channel](https://discord.gg/TegSJDD)
28 | * Email: kivydevelopment@gmail.com
29 |
30 |
31 | Installation and use with Buildozer
32 | ===================================
33 |
34 | #### Dependencies:
35 | * Kivy version is not less than 1.10.1
36 | * PIL
37 | * Python 3 (Python 2 not supported)
38 |
39 | #### How to install
40 |
41 | To install KivyMD, clone the project and run the setup.py script. The following line works on Linux and Mac OS, other OSes not tested:
42 |
43 | sudo python ./setup.py install
44 |
45 | Replace "python" with the Python interpreter you want to install KivyMD on (Python 3 is supported)
46 |
47 |
48 | #### How to use with Buildozer
49 |
50 | If you want to use KivyMD with buildozer, in your buildozer.spec's requirements line you should add the full git HTTPS address, like this example:
51 |
52 | requirements = kivy==master,git+https://github.com/HeaTTheatR/KivyMD.git
53 |
54 | Running on Android
55 | ==================
56 | Android 6.0 and higher [kitchen_sink-0.98.4-x86.apk](https://github.com/HeaTTheatR/KivyMD-data/tree/master/bin/x86) or [kitchen_sink-0.98.4-armeabi-v7a.apk](https://github.com/HeaTTheatR/KivyMD-data/tree/master/bin/armeabi-v7a)
57 |
58 | Packages for Android are built according to the following instructions:
59 | =======================================================================
60 |
61 | Download [XUbuntu 18.04](https://xubuntu.org/release/18-04/):
62 |
63 |
64 |
65 |
66 |
67 | * Create a new virtual machine based on the downloaded image of XUbuntu
68 | * Start the XUbuntu virtual machine, open the browser and download [this bash script](https://github.com/HeaTTheatR/KivyMD-data/blob/master/install-kivy-buildozer-dependencies.sh):
69 |
70 | Add execution permissions:
71 | ```bash
72 | chmod +x install-kivy-buildozer-dependencies.sh
73 | ```
74 | ...and run script:
75 |
76 | ```bash
77 | ./install-kivy-buildozer-dependencies.sh
78 | ```
79 |
80 | * Run the script - it will install all the necessary libraries and tools for creating packages for Android
81 | * Done! Now you have a virtual machine for building Kivy application packages!
82 | * Or see the instructions [here](https://github.com/zaemiel/kivy-buildozer-installer)
83 |
84 | What's new in version 0.100.0:
85 | ============================
86 | [CHANGELOG.md](https://github.com/HeaTTheatR/KivyMD/blob/master/CHANGELOG.md)
87 |
88 | API Breaking changes:
89 | =====================
90 | * Moving classes to new modules:
91 |
92 | | Old | New |
93 | |------------------------------------------------------|----------------------------------------------------|
94 | | `kivymd.tabs.MDBottomNavigation` | `kivymd.bottomnavigation.MDBottomNavigation` |
95 | | `kivymd.updatespinner` | `kivymd.refreshlayout` |
96 |
97 | * Renamed files, classes and variables:
98 |
99 | | Old | New |
100 | |------------------------------------------------------|-------------------------------------|
101 | | `kivymd.elevationbehavior` | `kivymd.elevation` |
102 | | `kivymd.grid` | `kivymd.imagelists` |
103 | | `kivymd.date_picker` | `kivymd.pickers` |
104 | | `kivymd.time_picker` | `kivymd.pickers` |
105 | | `kivymd.time_picker` | `kivymd.pickers` |
106 | | `kivymd.card` | `kivymd.cards` |
107 | | `kivymd.menu` | `kivymd.menus` |
108 | | `kivymd.snackbar` | `kivymd.snackbars` |
109 | | `Toolbar` (from `kivymd.toolbar`) | `MDToolbar` (from `kivymd.toolbar`) |
110 |
111 | * Changed font styles:
112 |
113 | | Old | New |
114 | |----------|-----------|
115 | | Icon | Icon |
116 | | - | Overline |
117 | | Caption | Caption |
118 | | Button | Button |
119 | | Body2 | Body2 |
120 | | Body1 | Body1 |
121 | | - | Subtitle2 |
122 | | Subhead | Subtitle1 |
123 | | Title | H6 |
124 | | Headline | H5 |
125 | | Display1 | H4 |
126 | | Display2 | H3 |
127 | | Display3 | H2 |
128 | | Display4 | H1 |
129 |
130 | * Colors `BlueGrey` and `Grey` renamed to `BlueGray` and `Gray` (for better fit MD spec)
131 |
132 |
133 | Video previous
134 | ==============
135 |
136 |
137 |
138 |
139 | Image previous
140 | ==============
141 |
142 |
143 |
144 |
145 | Sister project:
146 | ==============
147 |
148 | [Creator Kivy Project](https://github.com/HeaTTheatR/CreatorKivyProject) - Wizard for creating a new project for applications written using the Kivy framework
149 |
150 | License
151 | =======
152 |
153 | MIT, same as Kivy.
154 |
155 | Roboto font is licensed and distributed under the terms of the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
156 |
157 | [Material Design Iconic Font](https://github.com/zavoloklom/material-design-iconic-font) by [Sergey Kupletsky](https://twitter.com/zavoloklom) covered by the licenses described at https://zavoloklom.github.io/material-design-iconic-font/license.html.
158 |
159 | Icons by the materialdesignicons.com community covered by SIL OFL 1.1
160 |
--------------------------------------------------------------------------------
/kivymd/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | KivyMD
3 | ======
4 |
5 | KivyMD is a collection of Material Design compliant widgets for use with Kivy,
6 | a framework for cross-platform, touch-enabled graphical applications.
7 | The project's goal is to approximate Google's Material Design spec as close
8 | as possible without sacrificing ease of use or application performance.
9 |
10 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
11 | KivyMD library up to version 0.1.2
12 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
13 | KivyMD library version 0.1.3 and higher
14 |
15 | For suggestions and questions:
16 |
17 |
18 | This file is distributed under the terms of the same license,
19 | as the Kivy framework.
20 | """
21 |
22 | import os
23 |
24 | from kivy import Logger
25 |
26 | __version_info__ = (0, 100, 0)
27 | __version__ = '0.100.0'
28 |
29 | path = os.path.dirname(__file__)
30 | fonts_path = os.path.join(path, f'fonts{os.sep}')
31 | images_path = os.path.join(path, f'images{os.sep}')
32 |
33 | Logger.info(f'KivyMD: KivyMD version: {__version__}')
34 |
--------------------------------------------------------------------------------
/kivymd/accordionlistitem.py:
--------------------------------------------------------------------------------
1 | """
2 | Accordion List Item
3 | ===================
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 |
16 | Example
17 | -------
18 |
19 | from kivy.app import App
20 | from kivy.lang import Builder
21 | from kivy.factory import Factory
22 | from kivy.properties import ObjectProperty
23 | from kivy.uix.boxlayout import BoxLayout
24 |
25 | from kivymd.button import MDIconButton
26 | from kivymd.list import ILeftBodyTouch
27 | from kivymd.theming import ThemeManager
28 | from kivymd.accordionlistitem import MDAccordionListItem
29 | from kivymd.toast import toast
30 |
31 | Builder.load_string('''
32 | #:import MDToolbar kivymd.toolbar.MDToolbar
33 | #:import get_hex_from_color kivy.utils.get_hex_from_color
34 | #:import TwoLineIconListItem kivymd.list.TwoLineIconListItem
35 | #:import OneLineIconListItem kivymd.list.OneLineIconListItem
36 | #:import MDRoundFlatButton kivymd.button.MDRoundFlatButton
37 |
38 |
39 |
40 | orientation: 'vertical'
41 | padding: dp(10)
42 | spacing: dp(10)
43 | size_hint_y: None
44 | height: self.minimum_height
45 |
46 | BoxLayout:
47 | size_hint_y: None
48 | height: self.minimum_height
49 |
50 | Widget:
51 | MDRoundFlatButton:
52 | text: "Free call"
53 | on_press: root.callback(self.text)
54 | Widget:
55 | MDRoundFlatButton:
56 | text: "Free message"
57 | on_press: root.callback(self.text)
58 | Widget:
59 |
60 | OneLineIconListItem:
61 | text: "Video call"
62 | on_press: root.callback(self.text)
63 | IconLeftSampleWidget:
64 | icon: 'camera-front-variant'
65 |
66 | TwoLineIconListItem:
67 | text: "Call Viber Out"
68 | on_press: root.callback(self.text)
69 | secondary_text:
70 | "[color=%s]Advantageous rates for calls[/color]"\
71 | % get_hex_from_color(app.theme_cls.primary_color)
72 | IconLeftSampleWidget:
73 | icon: 'phone'
74 |
75 | TwoLineIconListItem:
76 | text: "Call over mobile network"
77 | on_press: root.callback(self.text)
78 | secondary_text:
79 | "[color=%s]Operator's tariffs apply[/color]"\
80 | % get_hex_from_color(app.theme_cls.primary_color)
81 | IconLeftSampleWidget:
82 | icon: 'remote'
83 |
84 |
85 |
86 | orientation: 'vertical'
87 |
88 | MDToolbar:
89 | id: toolbar
90 | title: app.title
91 | md_bg_color: app.theme_cls.primary_color
92 | background_palette: 'Primary'
93 | elevation: 10
94 | left_action_items: [['dots-vertical', lambda x: None]]
95 |
96 | Screen:
97 |
98 | ScrollView:
99 |
100 | GridLayout:
101 | id: anim_list
102 | cols: 1
103 | size_hint_y: None
104 | height: self.minimum_height
105 | ''')
106 |
107 |
108 | class ContentForAnimCard(BoxLayout):
109 | callback = ObjectProperty(lambda x: None)
110 |
111 |
112 | class IconLeftSampleWidget(ILeftBodyTouch, MDIconButton):
113 | pass
114 |
115 |
116 | class Example(App):
117 | theme_cls = ThemeManager()
118 | theme_cls.primary_palette = 'Blue'
119 | title = "Example Accordion List"
120 | main_widget = None
121 |
122 | def build(self):
123 | self.main_widget = Factory.ExampleAccordionList()
124 | return self.main_widget
125 |
126 | def on_start(self):
127 | def callback(text):
128 | toast(f'{text} to {content.name_item}')
129 |
130 | content = ContentForAnimCard(callback=callback)
131 | names_contacts = (
132 | 'Alexandr Taylor', 'Yuri Ivanov', 'Robert Patric', 'Bob Marley',
133 | 'Magnus Carlsen', 'Jon Romero', 'Anna Bell', 'Maxim Kramerer',
134 | 'Sasha Gray', 'Vladimir Ivanenko')
135 |
136 | for name_contact in names_contacts:
137 | self.main_widget.ids.anim_list.add_widget(
138 | MDAccordionListItem(content=content,
139 | icon='data/logo/kivy-icon-128.png',
140 | title=name_contact))
141 |
142 |
143 | Example().run()
144 | """
145 |
146 | from kivy.lang import Builder
147 | from kivy.animation import Animation
148 | from kivy.metrics import dp
149 | from kivy.properties import ObjectProperty, NumericProperty, StringProperty
150 | from kivy.uix.boxlayout import BoxLayout
151 | from kivy.uix.image import Image
152 |
153 | from kivymd.button import MDIconButton
154 | from kivymd.list import IRightBodyTouch, OneLineAvatarIconListItem, ILeftBody
155 |
156 | Builder.load_string('''
157 |
158 | text: root.title
159 |
160 | AvatarLeft:
161 | source: root.icon
162 |
163 | ChevronRight:
164 | icon: 'chevron-right'
165 | disabled: True
166 |
167 | canvas.before:
168 | PushMatrix
169 | Rotate:
170 | angle: self.angle
171 | axis: (0, 0, 1)
172 | origin: self.center
173 | canvas.after:
174 | PopMatrix
175 |
176 |
177 |
178 | size_hint_y: None
179 | height: dp(68)
180 |
181 | BoxLayout:
182 | id: box_item
183 | size_hint_y: None
184 | height: root.height
185 | orientation: 'vertical'
186 |
187 | AccordionListItem:
188 | id: item_anim
189 | title: root.title
190 | icon: root.icon
191 | _no_ripple_effect: True
192 | on_press: root.check_open_box(self)
193 | ''')
194 |
195 |
196 | class AvatarLeft(ILeftBody, Image):
197 | pass
198 |
199 |
200 | class ChevronRight(IRightBodyTouch, MDIconButton):
201 | angle = NumericProperty(0)
202 |
203 |
204 | class AccordionListItem(OneLineAvatarIconListItem):
205 | title = StringProperty()
206 | icon = StringProperty()
207 |
208 |
209 | class MDAccordionListItem(BoxLayout):
210 | content = ObjectProperty()
211 | icon = StringProperty()
212 | title = StringProperty()
213 |
214 | def check_open_box(self, instance):
215 | press_current_item = False
216 |
217 | for box in self.parent.children:
218 | if len(box.ids.box_item.children) == 2:
219 | if instance is box.ids.item_anim:
220 | press_current_item = True
221 | box.ids.box_item.remove_widget(box.ids.box_item.children[0])
222 | chevron = box.ids.box_item.children[0].children[0].children[0]
223 | self.anim_chevron_up(chevron)
224 | self.anim_resize_close(box)
225 | break
226 |
227 | if not press_current_item:
228 | self.anim_chevron_down()
229 |
230 | def anim_chevron_down(self):
231 | chevron = self.ids.item_anim.children[0].children[0]
232 | angle = -90
233 | Animation(angle=angle, d=.2).start(chevron)
234 | self.anim_resize_open_item()
235 |
236 | def anim_chevron_up(self, inctance):
237 | angle = 0
238 | Animation(angle=angle, d=.2).start(inctance)
239 |
240 | def anim_resize_close(self, box):
241 | Animation(height=dp(68), d=.1, t='in_cubic').start(box)
242 |
243 | def anim_resize_open_item(self, *args):
244 | self.content.name_item = self.title
245 | anim = Animation(height=self.content.height + dp(70),
246 | d=.2, t='in_cubic')
247 | anim.bind(on_complete=self.add_content)
248 | anim.start(self)
249 |
250 | def add_content(self, *args):
251 | if self.content:
252 | self.ids.box_item.add_widget(self.content)
253 |
--------------------------------------------------------------------------------
/kivymd/backgroundcolorbehavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Background Color Behavior
3 | =========================
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.lang import Builder
18 | from kivy.properties import BoundedNumericProperty, ReferenceListProperty
19 | from kivy.properties import OptionProperty, ListProperty
20 | from kivy.uix.widget import Widget
21 | from kivy.utils import get_color_from_hex
22 | from kivymd.color_definitions import palette, hue, text_colors
23 |
24 | Builder.load_string('''
25 |
26 | canvas:
27 | Color:
28 | rgba: self.md_bg_color
29 | Rectangle:
30 | size: self.size
31 | pos: self.pos
32 | ''')
33 |
34 |
35 | class BackgroundColorBehavior(Widget):
36 | r = BoundedNumericProperty(1., min=.0, max=1.)
37 | g = BoundedNumericProperty(1., min=.0, max=1.)
38 | b = BoundedNumericProperty(1., min=.0, max=1.)
39 | a = BoundedNumericProperty(.0, min=.0, max=1.)
40 |
41 | md_bg_color = ReferenceListProperty(r, g, b, a)
42 |
43 |
44 | class SpecificBackgroundColorBehavior(BackgroundColorBehavior):
45 | background_palette = OptionProperty(
46 | 'Primary', options=['Primary', 'Accent', *palette])
47 | background_hue = OptionProperty('500', options=hue)
48 |
49 | specific_text_color = ListProperty([0, 0, 0, .87])
50 | specific_secondary_text_color = ListProperty([0, 0, 0, .87])
51 |
52 | def _update_specific_text_color(self, instance, value):
53 | if hasattr(self, 'theme_cls'):
54 | palette = {'Primary': self.theme_cls.primary_palette,
55 | 'Accent': self.theme_cls.accent_palette
56 | }.get(self.background_palette, self.background_palette)
57 | else:
58 | palette = {'Primary': 'Blue',
59 | 'Accent': 'Amber'
60 | }.get(self.background_palette, self.background_palette)
61 | color = get_color_from_hex(text_colors[palette][self.background_hue])
62 | secondary_color = color[:]
63 | # Check for black text (need to adjust opacity)
64 | if (color[0] + color[1] + color[2]) == 0:
65 | color[3] = .87
66 | secondary_color[3] = .54
67 | else:
68 | secondary_color[3] = .7
69 | self.specific_text_color = color
70 | self.specific_secondary_text_color = secondary_color
71 |
72 | def __init__(self, **kwargs):
73 | super().__init__(**kwargs)
74 | if hasattr(self, 'theme_cls'):
75 | self.theme_cls.bind(primary_palette=self._update_specific_text_color)
76 | self.theme_cls.bind(accent_palette=self._update_specific_text_color)
77 | self.theme_cls.bind(theme_style=self._update_specific_text_color)
78 | self.bind(background_hue=self._update_specific_text_color)
79 | self.bind(background_palette=self._update_specific_text_color)
80 | self._update_specific_text_color(None, None)
81 |
--------------------------------------------------------------------------------
/kivymd/bottomsheet.py:
--------------------------------------------------------------------------------
1 | """
2 | Bottom Sheets
3 | =============
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 |
16 | `Material Design spec, Sheets: bottom `_
17 |
18 | In this module there's the :class:`MDBottomSheet` class which will let you implement your own Material Design Bottom Sheets, and there are two classes called :class:`MDListBottomSheet` and :class:`MDGridBottomSheet` implementing the ones mentioned in the spec.
19 |
20 | Example
21 | -------
22 |
23 | .. note::
24 |
25 | These widgets are designed to be called from Python code only.
26 |
27 | For :class:`MDListBottomSheet`:
28 |
29 | .. code-block:: python
30 |
31 | bs = MDListBottomSheet()
32 | bs.add_item("Here's an item with text only", lambda x: x)
33 | bs.add_item("Here's an item with an icon", lambda x: x, icon='md-cast')
34 | bs.add_item("Here's another!", lambda x: x, icon='md-nfc')
35 | bs.open()
36 |
37 | For :class:`MDListBottomSheet`:
38 |
39 | .. code-block:: python
40 |
41 | bs = MDGridBottomSheet()
42 | bs.add_item("Facebook", lambda x: x, icon_src='./assets/facebook-box.png')
43 | bs.add_item("YouTube", lambda x: x, icon_src='./assets/youtube-play.png')
44 | bs.add_item("Twitter", lambda x: x, icon_src='./assets/twitter.png')
45 | bs.add_item("Da Cloud", lambda x: x, icon_src='./assets/cloud-upload.png')
46 | bs.add_item("Camera", lambda x: x, icon_src='./assets/camera.png')
47 | bs.open()
48 | """
49 |
50 | from kivy.clock import Clock
51 | from kivy.lang import Builder
52 | from kivy.metrics import dp
53 | from kivy.properties import ObjectProperty, StringProperty
54 | from kivy.uix.behaviors import ButtonBehavior
55 | from kivy.uix.boxlayout import BoxLayout
56 | from kivy.uix.floatlayout import FloatLayout
57 | from kivy.uix.gridlayout import GridLayout
58 | from kivy.uix.modalview import ModalView
59 |
60 | from kivymd import images_path
61 | from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
62 | from kivymd.label import MDIcon
63 | from kivymd.list import MDList, OneLineListItem, ILeftBody,\
64 | OneLineIconListItem
65 | from kivymd.theming import ThemableBehavior
66 |
67 | Builder.load_string('''
68 |
69 | md_bg_color: 0, 0, 0, .8
70 | upper_padding: upper_padding
71 | gl_content: gl_content
72 |
73 | BoxLayout:
74 | size_hint_y: None
75 | orientation: 'vertical'
76 | padding: 0, 1, 0, 0
77 | height: upper_padding.height + gl_content.height + 1
78 |
79 | BsPadding:
80 | id: upper_padding
81 | size_hint_y: None
82 | height: root.height - min(root.width * 9 / 16, gl_content.height)
83 | on_release: root.dismiss()
84 |
85 | BottomSheetContent:
86 | id: gl_content
87 | size_hint_y: None
88 | md_bg_color: root.theme_cls.bg_normal
89 | cols: 1
90 | ''')
91 |
92 |
93 | class BsPadding(ButtonBehavior, FloatLayout):
94 | pass
95 |
96 |
97 | class BottomSheetContent(BackgroundColorBehavior, GridLayout):
98 | pass
99 |
100 |
101 | class MDBottomSheet(ThemableBehavior, ModalView):
102 | background = f'{images_path}transparent.png'
103 | upper_padding = ObjectProperty()
104 | gl_content = ObjectProperty()
105 |
106 | def open(self, *largs):
107 | super().open(*largs)
108 |
109 | def add_widget(self, widget, index=0, canvas=None):
110 | super().add_widget(widget, index, canvas)
111 |
112 |
113 | Builder.load_string('''
114 | #:import md_icons kivymd.icon_definitions.md_icons
115 |
116 |
117 |
118 | halign: 'center'
119 | theme_text_color: 'Primary'
120 | valign: 'middle'
121 | ''')
122 |
123 |
124 | class ListBSIconLeft(ILeftBody, MDIcon):
125 | pass
126 |
127 |
128 | class MDListBottomSheet(MDBottomSheet):
129 | mlist = ObjectProperty()
130 |
131 | def __init__(self, **kwargs):
132 | super().__init__(**kwargs)
133 | self.mlist = MDList()
134 | self.gl_content.add_widget(self.mlist)
135 | Clock.schedule_once(self.resize_content_layout, 0)
136 |
137 | def resize_content_layout(self, *largs):
138 | self.gl_content.height = self.mlist.height
139 |
140 | def add_item(self, text, callback, icon=None):
141 | if icon:
142 | item = OneLineIconListItem(text=text, on_release=callback)
143 | item.add_widget(ListBSIconLeft(icon=icon))
144 | else:
145 | item = OneLineListItem(text=text, on_release=callback)
146 | item.bind(on_release=lambda x: self.dismiss())
147 | self.mlist.add_widget(item)
148 |
149 |
150 | Builder.load_string('''
151 | #:import MDLabel kivymd.label.MDLabel
152 |
153 |
154 |
155 | orientation: 'vertical'
156 | padding: 0, dp(24), 0, 0
157 | size_hint_y: None
158 | size: dp(64), dp(96)
159 |
160 | BoxLayout:
161 | padding: dp(8), 0, dp(8), dp(8)
162 | size_hint_y: None
163 | height: dp(48)
164 |
165 | Image:
166 | source: root.source
167 |
168 | MDLabel:
169 | font_style: 'Caption'
170 | theme_text_color: 'Secondary'
171 | text: root.caption
172 | halign: 'center'
173 | ''')
174 |
175 |
176 | class GridBSItem(ButtonBehavior, BoxLayout):
177 | source = StringProperty()
178 | caption = StringProperty()
179 |
180 |
181 | class MDGridBottomSheet(MDBottomSheet):
182 | def __init__(self, **kwargs):
183 | super().__init__(**kwargs)
184 | self.gl_content.padding = (dp(16), 0, dp(16), dp(24))
185 | self.gl_content.height = dp(24)
186 | self.gl_content.cols = 3
187 |
188 | def add_item(self, text, callback, icon_src):
189 | item = GridBSItem(caption=text, on_release=callback, source=icon_src)
190 | item.bind(on_release=lambda x: self.dismiss())
191 | if len(self.gl_content.children) % 3 == 0:
192 | self.gl_content.height += dp(96)
193 | self.gl_content.add_widget(item)
194 |
--------------------------------------------------------------------------------
/kivymd/card.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from kivy.lang import Builder
3 | from kivy.properties import BoundedNumericProperty, ReferenceListProperty, ListProperty,BooleanProperty
4 | from kivy.uix.boxlayout import BoxLayout
5 | from kivymd.elevationbehavior import RectangularElevationBehavior
6 | from kivymd.theming import ThemableBehavior
7 | from kivy.metrics import dp
8 | from kivy.uix.widget import Widget
9 |
10 | Builder.load_string('''
11 |
12 | canvas:
13 | Color:
14 | rgba: self.md_bg_color
15 | RoundedRectangle:
16 | size: self.size
17 | pos: self.pos
18 | radius: [self.border_radius]
19 | Color:
20 | rgba: self.theme_cls.divider_color
21 | a: self.border_color_a
22 | Line:
23 | rounded_rectangle: (self.pos[0],self.pos[1],self.size[0],self.size[1],self.border_radius)
24 | md_bg_color: self.theme_cls.bg_light
25 |
26 |
27 | canvas:
28 | Color:
29 | rgba: self.theme_cls.divider_color
30 | Rectangle:
31 | size: self.size
32 | pos: self.pos
33 | ''')
34 |
35 |
36 | class MDSeparator(ThemableBehavior, BoxLayout):
37 | """ A separator line """
38 | def __init__(self, *args, **kwargs):
39 | super(MDSeparator, self).__init__(*args, **kwargs)
40 | self.on_orientation()
41 |
42 | def on_orientation(self,*args):
43 | self.size_hint = (1, None) if self.orientation == 'horizontal' else (None, 1)
44 | if self.orientation == 'horizontal':
45 | self.height = dp(1)
46 | else:
47 | self.width = dp(1)
48 |
49 |
50 | class MDCard(ThemableBehavior, RectangularElevationBehavior, BoxLayout):
51 | r = BoundedNumericProperty(1., min=0., max=1.)
52 | g = BoundedNumericProperty(1., min=0., max=1.)
53 | b = BoundedNumericProperty(1., min=0., max=1.)
54 | a = BoundedNumericProperty(0., min=0., max=1.)
55 |
56 | border_radius = BoundedNumericProperty(dp(3),min=0)
57 | border_color_a = BoundedNumericProperty(0, min=0., max=1.)
58 | md_bg_color = ReferenceListProperty(r, g, b, a)
59 |
--------------------------------------------------------------------------------
/kivymd/chips.py:
--------------------------------------------------------------------------------
1 | """
2 | Chips
3 | =====
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 |
13 | `Material Design spec, Chips `_
14 |
15 | Example
16 | -------
17 |
18 | from kivy.app import App
19 | from kivy.lang import Builder
20 |
21 | from kivymd.theming import ThemeManager
22 |
23 | kv = '''
24 | #:import MDToolbar kivymd.toolbar.MDToolbar
25 | #:import MDChip kivymd.chips.MDChip
26 | #:import MDChooseChip kivymd.chips.MDChooseChip
27 | #:import MDSeparator kivymd.cards.MDSeparator
28 | #:import MDLabel kivymd.label.MDLabel
29 |
30 |
31 | BoxLayout:
32 | orientation: 'vertical'
33 | spacing: dp(10)
34 |
35 | MDToolbar:
36 | title: 'Example Chips'
37 | md_bg_color: app.theme_cls.primary_color
38 | left_action_items: [['menu', lambda x: x]]
39 | background_palette: 'Primary'
40 |
41 | ScrollView:
42 |
43 | GridLayout:
44 | padding: dp(10)
45 | spacing: dp(10)
46 | cols: 1
47 | size_hint_y: None
48 | height: self.minimum_height
49 |
50 | MDLabel:
51 | text: 'Chips with color:'
52 |
53 | MDSeparator:
54 |
55 | StackLayout:
56 | size_hint_y: None
57 | height: self.minimum_height
58 | spacing: dp(5)
59 |
60 | MDChip:
61 | label: 'Coffee'
62 | color: .4470588235294118, .19607843137254902, 0, 1
63 | icon: 'coffee'
64 | callback: app.callback
65 |
66 | MDChip:
67 | label: 'Duck'
68 | color: .9215686274509803, 0, 0, 1
69 | icon: 'duck'
70 | callback: app.callback
71 |
72 | MDChip:
73 | label: 'Earth'
74 | color: .21176470588235294, .09803921568627451, 1, 1
75 | icon: 'earth'
76 | callback: app.callback
77 |
78 | MDChip:
79 | label: 'Face'
80 | color: .20392156865098, .48235294117606, .43529411764705883, 1
81 | icon: 'face'
82 | callback: app.callback
83 |
84 | MDChip:
85 | label: 'Facebook'
86 | color: .5607843137254902, .48235294164706, .435294117705883, 1
87 | icon: 'facebook'
88 | callback: app.callback
89 |
90 | Widget:
91 | size_hint_y: None
92 | height: dp(5)
93 |
94 | MDLabel:
95 | text: 'Chip without icon:'
96 |
97 | MDSeparator:
98 |
99 | StackLayout:
100 | size_hint_y: None
101 | height: self.minimum_height
102 | spacing: dp(5)
103 |
104 | MDChip:
105 | label: 'Without icon'
106 | icon: ''
107 | callback: app.callback
108 |
109 | Widget:
110 | size_hint_y: None
111 | height: dp(5)
112 |
113 | MDLabel:
114 | text: 'Chips with check:'
115 |
116 | MDSeparator:
117 |
118 | StackLayout:
119 | size_hint_y: None
120 | height: self.minimum_height
121 | spacing: dp(5)
122 |
123 | MDChip:
124 | label: 'Check'
125 | icon: ''
126 | check: True
127 | callback: app.callback
128 |
129 | MDChip:
130 | label: 'Check with icon'
131 | icon: 'city'
132 | check: True
133 | callback: app.callback
134 | Widget:
135 | size_hint_y: None
136 | height: dp(5)
137 |
138 | MDLabel:
139 | text: 'Choose chip:'
140 |
141 | MDSeparator:
142 |
143 | MDChooseChip:
144 |
145 | MDChip:
146 | label: 'Earth'
147 | icon: 'earth'
148 | callback: app.callback
149 |
150 | MDChip:
151 | label: 'Face'
152 | icon: 'face'
153 | callback: app.callback
154 |
155 | MDChip:
156 | label: 'Facebook'
157 | icon: 'facebook'
158 | callback: app.callback
159 | '''
160 |
161 |
162 | class MyApp(App):
163 | theme_cls = ThemeManager()
164 | theme_cls.primary_palette = 'Red'
165 |
166 | def callback(self, name_chip):
167 | pass
168 |
169 | def build(self):
170 | return Builder.load_string(kv)
171 |
172 |
173 | MyApp().run()
174 | """
175 |
176 | from kivy.metrics import dp
177 | from kivy.properties import StringProperty, ListProperty, ObjectProperty,\
178 | BooleanProperty
179 | from kivy.uix.boxlayout import BoxLayout
180 | from kivy.lang import Builder
181 | from kivy.uix.stacklayout import StackLayout
182 |
183 | from kivymd.button import MDIconButton
184 | from kivymd.theming import ThemableBehavior
185 |
186 | Builder.load_string('''
187 | #:import MDIconButton kivymd.button.MDIconButton
188 |
189 |
190 |
191 | size_hint_y: None
192 | height: self.minimum_height
193 | spacing: dp(5)
194 |
195 |
196 |
197 | size_hint: None, None
198 | height: dp(26)
199 | width:
200 | self.minimum_width - dp(10) if root.icon != 'checkbox-blank-circle'\
201 | else self.minimum_width
202 |
203 | canvas:
204 | Color:
205 | rgba: root.color
206 | RoundedRectangle:
207 | pos: self.pos
208 | size: self.size
209 | radius: [15, ]
210 |
211 | BoxLayout:
212 | id: box_check
213 | size_hint: None, None
214 | size: self.minimum_size
215 | pos_hint: {'center_y': .5}
216 |
217 | BoxLayout:
218 | size_hint_x: None
219 | width: self.minimum_width
220 | padding: dp(10)
221 |
222 | Label:
223 | id: label
224 | text: root.label
225 | size_hint_x: None
226 | width: self.texture_size[0]
227 |
228 | MDIconButton:
229 | id: icon
230 | icon: root.icon
231 | size_hint_y: None
232 | height: dp(26)
233 | disabled: True
234 | ''')
235 |
236 |
237 | class MDChip(BoxLayout, ThemableBehavior):
238 | label = StringProperty()
239 | icon = StringProperty('checkbox-blank-circle')
240 | color = ListProperty([.4, .4, .4, 1])
241 | check = BooleanProperty(False)
242 | callback = ObjectProperty(lambda x: None)
243 |
244 | def on_icon(self, instance, value):
245 | if value == '':
246 | self.icon = 'checkbox-blank-circle'
247 | self.remove_widget(self.ids.icon)
248 |
249 | def on_touch_down(self, touch):
250 | if self.collide_point(*touch.pos):
251 | self.callback(self.label)
252 | md_choose_chip = self.parent
253 | if md_choose_chip.__class__ is MDChooseChip:
254 | if md_choose_chip.selected_chip:
255 | md_choose_chip.selected_chip.color =\
256 | md_choose_chip.selected_chip_color
257 | md_choose_chip.selected_chip = self
258 | md_choose_chip.selected_chip_color = self.color
259 | self.color = self.theme_cls.primary_color
260 | if self.check:
261 | if not len(self.ids.box_check.children):
262 | self.ids.box_check.add_widget(
263 | MDIconButton(icon='check', size_hint_y=None,
264 | height=dp(26),
265 | disabled=True))
266 | else:
267 | check = self.ids.box_check.children[0]
268 | self.ids.box_check.remove_widget(check)
269 |
270 |
271 | class MDChooseChip(StackLayout):
272 | selected_chip = None
273 | selected_chip_color = None
274 |
--------------------------------------------------------------------------------
/kivymd/elevation.py:
--------------------------------------------------------------------------------
1 | """
2 | Elevation Behavior
3 | ==================
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.app import App
18 | from kivy.lang import Builder
19 | from kivy.properties import (ListProperty, ObjectProperty, NumericProperty)
20 | from kivy.properties import AliasProperty
21 | from kivy.metrics import dp
22 |
23 | Builder.load_string('''
24 |
25 | canvas.before:
26 | Color:
27 | a: self._soft_shadow_a
28 | Rectangle:
29 | texture: self._soft_shadow_texture
30 | size: self._soft_shadow_size
31 | pos: self._soft_shadow_pos
32 | Color:
33 | a: self._hard_shadow_a
34 | Rectangle:
35 | texture: self._hard_shadow_texture
36 | size: self._hard_shadow_size
37 | pos: self._hard_shadow_pos
38 | Color:
39 | a: 1
40 |
41 |
42 |
43 | canvas.before:
44 | Color:
45 | a: self._soft_shadow_a
46 | Rectangle:
47 | texture: self._soft_shadow_texture
48 | size: self._soft_shadow_size
49 | pos: self._soft_shadow_pos
50 | Color:
51 | a: self._hard_shadow_a
52 | Rectangle:
53 | texture: self._hard_shadow_texture
54 | size: self._hard_shadow_size
55 | pos: self._hard_shadow_pos
56 | Color:
57 | a: 1
58 | ''')
59 |
60 |
61 | class CommonElevationBehavior(object):
62 | _elevation = NumericProperty(1)
63 |
64 | def _get_elevation(self):
65 | return self._elevation
66 |
67 | def _set_elevation(self, elevation):
68 | try:
69 | self._elevation = elevation
70 | except KeyError:
71 | self._elevation = 1
72 |
73 | elevation = AliasProperty(_get_elevation, _set_elevation,
74 | bind=('_elevation', ))
75 |
76 | _soft_shadow_texture = ObjectProperty()
77 | _soft_shadow_size = ListProperty([0, 0])
78 | _soft_shadow_pos = ListProperty([0, 0])
79 | _soft_shadow_a = NumericProperty(0)
80 | _hard_shadow_texture = ObjectProperty()
81 | _hard_shadow_size = ListProperty([0, 0])
82 | _hard_shadow_pos = ListProperty([0, 0])
83 | _hard_shadow_a = NumericProperty(0)
84 |
85 | def __init__(self, **kwargs):
86 | super().__init__(**kwargs)
87 | self.bind(elevation=self._update_shadow,
88 | pos=self._update_shadow,
89 | size=self._update_shadow)
90 |
91 | def _update_shadow(self, *args):
92 | raise NotImplemented
93 |
94 |
95 | class RectangularElevationBehavior(CommonElevationBehavior):
96 | def _update_shadow(self, *args):
97 | if self.elevation > 0:
98 | ratio = self.width / (self.height if self.height != 0 else 1)
99 | if -2 < ratio < 2:
100 | self._shadow = App.get_running_app().theme_cls.quad_shadow
101 | width = soft_width = self.width * 1.9
102 | height = soft_height = self.height * 1.9
103 | elif ratio <= -2:
104 | self._shadow = App.get_running_app().theme_cls.rec_st_shadow
105 | ratio = abs(ratio)
106 | if ratio > 5:
107 | ratio = ratio * 22
108 | else:
109 | ratio = ratio * 11.5
110 |
111 | width = soft_width = self.width * 1.9
112 | height = self.height + dp(ratio)
113 | soft_height = self.height + dp(ratio) + dp(self.elevation) * .5
114 | else:
115 | self._shadow = App.get_running_app().theme_cls.quad_shadow
116 | width = soft_width = self.width * 1.8
117 | height = soft_height = self.height * 1.8
118 |
119 | x = self.center_x - width / 2
120 | soft_x = self.center_x - soft_width / 2
121 | self._soft_shadow_size = (soft_width, soft_height)
122 | self._hard_shadow_size = (width, height)
123 |
124 | y = self.center_y - soft_height / 2 - dp(
125 | .1 * 1.5 ** self.elevation)
126 | self._soft_shadow_pos = (soft_x, y)
127 | self._soft_shadow_a = .1 * 1.1 ** self.elevation
128 | self._soft_shadow_texture = self._shadow.textures[
129 | str(int(round(self.elevation - 1)))]
130 |
131 | y = self.center_y - height / 2 - dp(.5 * 1.18 ** self.elevation)
132 | self._hard_shadow_pos = (x, y)
133 | self._hard_shadow_a = .4 * .9 ** self.elevation
134 | self._hard_shadow_texture = self._shadow.textures[
135 | str(int(round(self.elevation)))]
136 |
137 | else:
138 | self._soft_shadow_a = 0
139 | self._hard_shadow_a = 0
140 |
141 |
142 | class CircularElevationBehavior(CommonElevationBehavior):
143 | def __init__(self, **kwargs):
144 | super().__init__(**kwargs)
145 | self._shadow = App.get_running_app().theme_cls.round_shadow
146 |
147 | def _update_shadow(self, *args):
148 | if self.elevation > 0:
149 | width = self.width * 2
150 | height = self.height * 2
151 |
152 | x = self.center_x - width / 2
153 | self._soft_shadow_size = (width, height)
154 |
155 | self._hard_shadow_size = (width, height)
156 |
157 | y = self.center_y - height / 2 - dp(.1 * 1.5 ** self.elevation)
158 | self._soft_shadow_pos = (x, y)
159 | self._soft_shadow_a = .1 * 1.1 ** self.elevation
160 | self._soft_shadow_texture = self._shadow.textures[
161 | str(int(round(self.elevation)))]
162 |
163 | y = self.center_y - height / 2 - dp(.5 * 1.18 ** self.elevation)
164 | self._hard_shadow_pos = (x, y)
165 | self._hard_shadow_a = .4 * .9 ** self.elevation
166 | self._hard_shadow_texture = self._shadow.textures[
167 | str(int(round(self.elevation - 1)))]
168 |
169 | else:
170 | self._soft_shadow_a = 0
171 | self._hard_shadow_a = 0
172 |
--------------------------------------------------------------------------------
/kivymd/elevationbehavior.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from kivy.app import App
4 | from kivy.lang import Builder
5 | from kivy.properties import (ListProperty, ObjectProperty, NumericProperty)
6 | from kivy.properties import AliasProperty
7 | from kivy.metrics import dp
8 |
9 | Builder.load_string('''
10 |
11 | canvas.before:
12 | Color:
13 | a: self._soft_shadow_a
14 | Rectangle:
15 | texture: self._soft_shadow_texture
16 | size: self._soft_shadow_size
17 | pos: self._soft_shadow_pos
18 | Color:
19 | a: self._hard_shadow_a
20 | Rectangle:
21 | texture: self._hard_shadow_texture
22 | size: self._hard_shadow_size
23 | pos: self._hard_shadow_pos
24 | Color:
25 | a: 1
26 |
27 |
28 | canvas.before:
29 | Color:
30 | a: self._soft_shadow_a
31 | Rectangle:
32 | texture: self._soft_shadow_texture
33 | size: self._soft_shadow_size
34 | pos: self._soft_shadow_pos
35 | Color:
36 | a: self._hard_shadow_a
37 | Rectangle:
38 | texture: self._hard_shadow_texture
39 | size: self._hard_shadow_size
40 | pos: self._hard_shadow_pos
41 | Color:
42 | a: 1
43 | ''')
44 |
45 |
46 | class CommonElevationBehavior(object):
47 | _elevation = NumericProperty(1)
48 |
49 | def _get_elevation(self):
50 | return self._elevation
51 |
52 | def _set_elevation(self, elevation):
53 | try:
54 | self._elevation = elevation
55 | except:
56 | self._elevation = 1
57 |
58 | elevation = AliasProperty(_get_elevation, _set_elevation,
59 | bind=('_elevation',))
60 |
61 | _soft_shadow_texture = ObjectProperty()
62 | _soft_shadow_size = ListProperty([0, 0])
63 | _soft_shadow_pos = ListProperty([0, 0])
64 | _soft_shadow_a = NumericProperty(0)
65 | _hard_shadow_texture = ObjectProperty()
66 | _hard_shadow_size = ListProperty([0, 0])
67 | _hard_shadow_pos = ListProperty([0, 0])
68 | _hard_shadow_a = NumericProperty(0)
69 |
70 | def __init__(self, **kwargs):
71 | super(CommonElevationBehavior, self).__init__(**kwargs)
72 | self.bind(elevation=self._update_shadow,
73 | pos=self._update_shadow,
74 | size=self._update_shadow)
75 |
76 | def _update_shadow(self, *args):
77 | raise NotImplemented
78 |
79 | class RectangularElevationBehavior(CommonElevationBehavior):
80 | def _update_shadow(self, *args):
81 | if self.elevation > 0:
82 | ratio = self.width / (self.height if self.height != 0 else 1)
83 | if ratio > -2 and ratio < 2:
84 | self._shadow = App.get_running_app().theme_cls.quad_shadow
85 | width = soft_width = self.width * 1.9
86 | height = soft_height = self.height * 1.9
87 | elif ratio <= -2:
88 | self._shadow = App.get_running_app().theme_cls.rec_st_shadow
89 | ratio = abs(ratio)
90 | if ratio > 5:
91 | ratio = ratio * 22
92 | else:
93 | ratio = ratio * 11.5
94 |
95 | width = soft_width = self.width * 1.9
96 | height = self.height + dp(ratio)
97 | soft_height = self.height + dp(ratio) + dp(self.elevation) * .5
98 | else:
99 | self._shadow = App.get_running_app().theme_cls.quad_shadow
100 | width = soft_width = self.width * 1.8
101 | height = soft_height = self.height * 1.8
102 | # self._shadow = App.get_running_app().theme_cls.rec_shadow
103 | # ratio = abs(ratio)
104 | # if ratio > 5:
105 | # ratio = ratio * 22
106 | # else:
107 | # ratio = ratio * 11.5
108 | #
109 | # width = self.width + dp(ratio)
110 | # soft_width = self.width + dp(ratio) + dp(self.elevation) * .9
111 | # height = soft_height = self.height * 1.9
112 |
113 | x = self.center_x - width / 2
114 | soft_x = self.center_x - soft_width / 2
115 | self._soft_shadow_size = (soft_width, soft_height)
116 | self._hard_shadow_size = (width, height)
117 |
118 | y = self.center_y - soft_height / 2 - dp(
119 | .1 * 1.5 ** self.elevation)
120 | self._soft_shadow_pos = (soft_x, y)
121 | self._soft_shadow_a = 0.1 * 1.1 ** self.elevation
122 | self._soft_shadow_texture = self._shadow.textures[
123 | str(int(round(self.elevation - 1)))]
124 |
125 | y = self.center_y - height / 2 - dp(.5 * 1.18 ** self.elevation)
126 | self._hard_shadow_pos = (x, y)
127 | self._hard_shadow_a = .4 * .9 ** self.elevation
128 | self._hard_shadow_texture = self._shadow.textures[
129 | str(int(round(self.elevation)))]
130 |
131 | else:
132 | self._soft_shadow_a = 0
133 | self._hard_shadow_a = 0
134 |
135 |
136 | class CircularElevationBehavior(CommonElevationBehavior):
137 | def __init__(self, **kwargs):
138 | super(CircularElevationBehavior, self).__init__(**kwargs)
139 | self._shadow = App.get_running_app().theme_cls.round_shadow
140 |
141 | def _update_shadow(self, *args):
142 | if self.elevation > 0:
143 | width = self.width * 2
144 | height = self.height * 2
145 |
146 | x = self.center_x - width / 2
147 | self._soft_shadow_size = (width, height)
148 |
149 | self._hard_shadow_size = (width, height)
150 |
151 | y = self.center_y - height / 2 - dp(.1 * 1.5 ** self.elevation)
152 | self._soft_shadow_pos = (x, y)
153 | self._soft_shadow_a = 0.1 * 1.1 ** self.elevation
154 | self._soft_shadow_texture = self._shadow.textures[
155 | str(int(round(self.elevation)))]
156 |
157 | y = self.center_y - height / 2 - dp(.5 * 1.18 ** self.elevation)
158 | self._hard_shadow_pos = (x, y)
159 | self._hard_shadow_a = .4 * .9 ** self.elevation
160 | self._hard_shadow_texture = self._shadow.textures[
161 | str(int(round(self.elevation - 1)))]
162 |
163 | else:
164 | self._soft_shadow_a = 0
165 | self._hard_shadow_a = 0
166 |
--------------------------------------------------------------------------------
/kivymd/font_definitions.py:
--------------------------------------------------------------------------------
1 | """
2 | Font Definitions
3 | ================
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 |
16 | `Material Design spec, The type system `_
17 | """
18 |
19 | from kivy.core.text import LabelBase
20 | from kivymd import fonts_path
21 |
22 | fonts = [
23 | {
24 | 'name': 'Roboto',
25 | 'fn_regular': fonts_path + 'Roboto-Regular.ttf',
26 | 'fn_bold': fonts_path + 'Roboto-Bold.ttf',
27 | 'fn_italic': fonts_path + 'Roboto-Italic.ttf',
28 | 'fn_bolditalic': fonts_path + 'Roboto-BoldItalic.ttf'
29 | },
30 | {
31 | 'name': 'RobotoThin',
32 | 'fn_regular': fonts_path + 'Roboto-Thin.ttf',
33 | 'fn_italic': fonts_path + 'Roboto-ThinItalic.ttf',
34 | },
35 | {
36 | 'name': 'RobotoLight',
37 | 'fn_regular': fonts_path + 'Roboto-Light.ttf',
38 | 'fn_italic': fonts_path + 'Roboto-LightItalic.ttf',
39 | },
40 | {
41 | 'name': 'RobotoMedium',
42 | 'fn_regular': fonts_path + 'Roboto-Medium.ttf',
43 | 'fn_italic': fonts_path + 'Roboto-MediumItalic.ttf',
44 | },
45 | {
46 | 'name': 'RobotoBlack',
47 | 'fn_regular': fonts_path + 'Roboto-Black.ttf',
48 | 'fn_italic': fonts_path + 'Roboto-BlackItalic.ttf',
49 | },
50 | {
51 | 'name': 'Icons',
52 | 'fn_regular': fonts_path + 'materialdesignicons-webfont.ttf',
53 | }
54 | ]
55 |
56 | for font in fonts:
57 | LabelBase.register(**font)
58 |
59 | theme_font_styles = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Subtitle1',
60 | 'Subtitle2', 'Body1', 'Body2', 'Button', 'Caption',
61 | 'Overline', 'Icon']
62 |
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/kivymd/fonts/materialdesignicons-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/fonts/materialdesignicons-webfont.ttf
--------------------------------------------------------------------------------
/kivymd/grid.py:
--------------------------------------------------------------------------------
1 | # coding=utf-8
2 | from kivy.lang import Builder
3 | from kivy.properties import StringProperty, BooleanProperty, ObjectProperty, \
4 | NumericProperty, ListProperty, OptionProperty
5 | from kivy.uix.behaviors import ButtonBehavior
6 | from kivy.uix.boxlayout import BoxLayout
7 | from kivy.uix.floatlayout import FloatLayout
8 | from kivymd.ripplebehavior import RectangularRippleBehavior
9 | from kivymd.theming import ThemableBehavior
10 |
11 | Builder.load_string("""
12 |
13 | _img_widget: img
14 | _img_overlay: img_overlay
15 | _box_overlay: box
16 | AsyncImage:
17 | id: img
18 | allow_stretch: root.allow_stretch
19 | anim_delay: root.anim_delay
20 | anim_loop: root.anim_loop
21 | color: root.img_color
22 | keep_ratio: root.keep_ratio
23 | mipmap: root.mipmap
24 | source: root.source
25 | size_hint_y: 1 if root.overlap else None
26 | x: root.x
27 | y: root.y if root.overlap or root.box_position == 'header' else box.top
28 | BoxLayout:
29 | id: img_overlay
30 | size_hint: img.size_hint
31 | size: img.size
32 | pos: img.pos
33 | BoxLayout:
34 | canvas:
35 | Color:
36 | rgba: root.box_color
37 | Rectangle:
38 | pos: self.pos
39 | size: self.size
40 | id: box
41 | size_hint_y: None
42 | height: dp(68) if root.lines == 2 else dp(48)
43 | x: root.x
44 | y: root.y if root.box_position == 'footer' else root.y + root.height - self.height
45 |
46 |
47 | _img_widget: img
48 | _img_overlay: img_overlay
49 | _box_overlay: box
50 | _box_label: boxlabel
51 | AsyncImage:
52 | id: img
53 | allow_stretch: root.allow_stretch
54 | anim_delay: root.anim_delay
55 | anim_loop: root.anim_loop
56 | color: root.img_color
57 | keep_ratio: root.keep_ratio
58 | mipmap: root.mipmap
59 | source: root.source
60 | size_hint_y: 1 if root.overlap else None
61 | x: root.x
62 | y: root.y if root.overlap or root.box_position == 'header' else box.top
63 | BoxLayout:
64 | id: img_overlay
65 | size_hint: img.size_hint
66 | size: img.size
67 | pos: img.pos
68 | BoxLayout:
69 | canvas:
70 | Color:
71 | rgba: root.box_color
72 | Rectangle:
73 | pos: self.pos
74 | size: self.size
75 | id: box
76 | size_hint_y: None
77 | height: dp(68) if root.lines == 2 else dp(48)
78 | x: root.x
79 | y: root.y if root.box_position == 'footer' else root.y + root.height - self.height
80 | MDLabel:
81 | id: boxlabel
82 | font_style: "Caption"
83 | halign: "center"
84 | text: root.text
85 | """)
86 |
87 |
88 | class Tile(ThemableBehavior, RectangularRippleBehavior, ButtonBehavior,
89 | BoxLayout):
90 | """A simple tile. It does nothing special, just inherits the right behaviors
91 | to work as a building block.
92 | """
93 | pass
94 |
95 |
96 | class SmartTile(ThemableBehavior, RectangularRippleBehavior, ButtonBehavior,
97 | FloatLayout):
98 | """A tile for more complex needs.
99 |
100 | Includes an image, a container to place overlays and a box that can act
101 | as a header or a footer, as described in the Material Design specs.
102 | """
103 |
104 | box_color = ListProperty([0, 0, 0, 0.5])
105 | """Sets the color and opacity for the information box."""
106 |
107 | box_position = OptionProperty('footer', options=['footer', 'header'])
108 | """Determines wether the information box acts as a header or footer to the
109 | image.
110 | """
111 |
112 | lines = OptionProperty(1, options=[1, 2])
113 | """Number of lines in the header/footer.
114 |
115 | As per Material Design specs, only 1 and 2 are valid values.
116 | """
117 |
118 | overlap = BooleanProperty(True)
119 | """Determines if the header/footer overlaps on top of the image or not"""
120 |
121 | # Img properties
122 | allow_stretch = BooleanProperty(True)
123 | anim_delay = NumericProperty(0.25)
124 | anim_loop = NumericProperty(0)
125 | img_color = ListProperty([1, 1, 1, 1])
126 | keep_ratio = BooleanProperty(False)
127 | mipmap = BooleanProperty(False)
128 | source = StringProperty()
129 |
130 | _img_widget = ObjectProperty()
131 | _img_overlay = ObjectProperty()
132 | _box_overlay = ObjectProperty()
133 | _box_label = ObjectProperty()
134 |
135 | def reload(self):
136 | self._img_widget.reload()
137 |
138 | def add_widget(self, widget, index=0):
139 | if issubclass(widget.__class__, IOverlay):
140 | self._img_overlay.add_widget(widget, index)
141 | elif issubclass(widget.__class__, IBoxOverlay):
142 | self._box_overlay.add_widget(widget, index)
143 | else:
144 | super(SmartTile, self).add_widget(widget, index)
145 |
146 |
147 | class SmartTileWithLabel(SmartTile):
148 | _box_label = ObjectProperty()
149 |
150 | # MDLabel properties
151 | font_style = StringProperty("Caption")
152 | theme_text_color = StringProperty("")
153 | text = StringProperty("")
154 | """Determines the text for the box footer/header"""
155 |
156 |
157 | class IBoxOverlay():
158 | """An interface to specify widgets that belong to to the image overlay
159 | in the :class:`SmartTile` widget when added as a child.
160 | """
161 | pass
162 |
163 |
164 | class IOverlay():
165 | """An interface to specify widgets that belong to to the image overlay
166 | in the :class:`SmartTile` widget when added as a child.
167 | """
168 | pass
169 |
--------------------------------------------------------------------------------
/kivymd/images/dialog_in_fade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/dialog_in_fade.png
--------------------------------------------------------------------------------
/kivymd/images/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/folder.png
--------------------------------------------------------------------------------
/kivymd/images/ios_bg_mod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/ios_bg_mod.png
--------------------------------------------------------------------------------
/kivymd/images/ios_bg_mod_for_toast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/ios_bg_mod_for_toast.png
--------------------------------------------------------------------------------
/kivymd/images/ios_entr_ti.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/ios_entr_ti.png
--------------------------------------------------------------------------------
/kivymd/images/kivy-logo-white-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/kivy-logo-white-512.png
--------------------------------------------------------------------------------
/kivymd/images/kivymd_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/kivymd_512.png
--------------------------------------------------------------------------------
/kivymd/images/kivymd_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/kivymd_logo.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/quad_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/quad_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/quad_shadow-2.png
--------------------------------------------------------------------------------
/kivymd/images/quad_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"quad_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "quad_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "quad_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}
--------------------------------------------------------------------------------
/kivymd/images/rec_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/rec_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/rec_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/rec_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/rec_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"rec_shadow-1.png": {"20": [2, 266, 256, 128], "21": [260, 266, 256, 128], "22": [518, 266, 256, 128], "23": [776, 266, 256, 128], "3": [260, 136, 256, 128], "2": [2, 136, 256, 128], "5": [776, 136, 256, 128], "4": [518, 136, 256, 128], "7": [260, 6, 256, 128], "6": [2, 6, 256, 128], "9": [776, 6, 256, 128], "8": [518, 6, 256, 128]}, "rec_shadow-0.png": {"11": [518, 266, 256, 128], "10": [260, 266, 256, 128], "13": [2, 136, 256, 128], "12": [776, 266, 256, 128], "15": [518, 136, 256, 128], "14": [260, 136, 256, 128], "17": [2, 6, 256, 128], "16": [776, 136, 256, 128], "19": [518, 6, 256, 128], "18": [260, 6, 256, 128], "1": [776, 6, 256, 128], "0": [2, 266, 256, 128]}}
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/rec_st_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/rec_st_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/rec_st_shadow-2.png
--------------------------------------------------------------------------------
/kivymd/images/rec_st_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"rec_st_shadow-0.png": {"11": [262, 138, 128, 256], "10": [132, 138, 128, 256], "13": [522, 138, 128, 256], "12": [392, 138, 128, 256], "15": [782, 138, 128, 256], "14": [652, 138, 128, 256], "16": [912, 138, 128, 256], "0": [2, 138, 128, 256]}, "rec_st_shadow-1.png": {"20": [522, 138, 128, 256], "21": [652, 138, 128, 256], "17": [2, 138, 128, 256], "23": [912, 138, 128, 256], "19": [262, 138, 128, 256], "18": [132, 138, 128, 256], "22": [782, 138, 128, 256], "1": [392, 138, 128, 256]}, "rec_st_shadow-2.png": {"3": [132, 138, 128, 256], "2": [2, 138, 128, 256], "5": [392, 138, 128, 256], "4": [262, 138, 128, 256], "7": [652, 138, 128, 256], "6": [522, 138, 128, 256], "9": [912, 138, 128, 256], "8": [782, 138, 128, 256]}}
--------------------------------------------------------------------------------
/kivymd/images/round_shadow-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/round_shadow-0.png
--------------------------------------------------------------------------------
/kivymd/images/round_shadow-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/round_shadow-1.png
--------------------------------------------------------------------------------
/kivymd/images/round_shadow-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/round_shadow-2.png
--------------------------------------------------------------------------------
/kivymd/images/round_shadow.atlas:
--------------------------------------------------------------------------------
1 | {"round_shadow-1.png": {"20": [2, 136, 128, 128], "21": [132, 136, 128, 128], "22": [262, 136, 128, 128], "23": [2, 6, 128, 128], "19": [132, 266, 128, 128], "18": [2, 266, 128, 128], "1": [262, 266, 128, 128], "3": [262, 6, 128, 128], "2": [132, 6, 128, 128]}, "round_shadow-0.png": {"11": [262, 266, 128, 128], "10": [132, 266, 128, 128], "13": [132, 136, 128, 128], "12": [2, 136, 128, 128], "15": [2, 6, 128, 128], "14": [262, 136, 128, 128], "17": [262, 6, 128, 128], "16": [132, 6, 128, 128], "0": [2, 266, 128, 128]}, "round_shadow-2.png": {"5": [132, 266, 128, 128], "4": [2, 266, 128, 128], "7": [2, 136, 128, 128], "6": [262, 266, 128, 128], "9": [262, 136, 128, 128], "8": [132, 136, 128, 128]}}
--------------------------------------------------------------------------------
/kivymd/images/swipe_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/swipe_shadow.png
--------------------------------------------------------------------------------
/kivymd/images/transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/images/transparent.png
--------------------------------------------------------------------------------
/kivymd/label.py:
--------------------------------------------------------------------------------
1 | """
2 | Label
3 | =====
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.lang import Builder
18 | from kivy.metrics import sp
19 | from kivy.properties import OptionProperty, ListProperty, BooleanProperty,\
20 | StringProperty, AliasProperty
21 | from kivy.uix.label import Label
22 |
23 | from kivymd.font_definitions import theme_font_styles
24 | from kivymd.material_resources import DEVICE_TYPE
25 | from kivymd.theming import ThemableBehavior
26 | from kivymd.theming_dynamic_text import get_contrast_text_color
27 |
28 | Builder.load_string('''
29 | #:import md_icons kivymd.icon_definitions.md_icons
30 |
31 |
32 |
33 | disabled_color: self.theme_cls.disabled_hint_text_color
34 | text_size: (self.width, None)
35 |
36 |
37 |
38 | font_style: 'Icon'
39 | text: u'{}'.format(md_icons[self.icon])
40 | ''')
41 |
42 |
43 | class MDLabel(ThemableBehavior, Label):
44 | font_style = OptionProperty('Body1', options=theme_font_styles)
45 |
46 | can_capitalize = BooleanProperty(True)
47 | _capitalizing = BooleanProperty(False)
48 |
49 | def _get_text(self):
50 | if self._capitalizing:
51 | return self._text.upper()
52 | return self._text
53 |
54 | def _set_text(self, value):
55 | self._text = value
56 |
57 | _text = StringProperty()
58 | text = AliasProperty(_get_text, _set_text, bind=['_text', '_capitalizing'])
59 |
60 | theme_text_color = OptionProperty(None, allownone=True,
61 | options=['Primary', 'Secondary', 'Hint',
62 | 'Error', 'Custom',
63 | 'ContrastParentBackground']
64 | )
65 |
66 | text_color = ListProperty(None, allownone=True)
67 |
68 | parent_background = ListProperty(None, allownone=True)
69 |
70 | _currently_bound_property = {}
71 |
72 | def __init__(self, **kwargs):
73 | super().__init__(**kwargs)
74 | self.bind(font_style=self.update_font_style,
75 | can_capitalize=self.update_font_style)
76 | self.on_theme_text_color(None, self.theme_text_color)
77 | self.update_font_style()
78 | self.on_opposite_colors(None, self.opposite_colors)
79 |
80 | def update_font_style(self, *args):
81 | font_info = self.theme_cls.font_styles[self.font_style]
82 | self.font_name = font_info[0]
83 | self.font_size = sp(font_info[1])
84 | if font_info[2] and self.can_capitalize:
85 | self._capitalizing = True
86 | else:
87 | self._capitalizing = False
88 | # TODO: Add letter spacing change
89 | # self.letter_spacing = font_info[3]
90 |
91 | def on_theme_text_color(self, instance, value):
92 | t = self.theme_cls
93 | op = self.opposite_colors
94 | setter = self.setter('color')
95 | t.unbind(**self._currently_bound_property)
96 | attr_name = {
97 | 'Primary': 'text_color' if not op else 'opposite_text_color',
98 | 'Secondary': 'secondary_text_color' if not op else
99 | 'opposite_secondary_text_color',
100 | 'Hint': 'disabled_hint_text_color' if not op else
101 | 'opposite_disabled_hint_text_color',
102 | 'Error': 'error_color',
103 | }.get(value, None)
104 | if attr_name:
105 | c = {attr_name: setter}
106 | t.bind(**c)
107 | self._currently_bound_property = c
108 | self.color = getattr(t, attr_name)
109 | else:
110 | # 'Custom' and 'ContrastParentBackground' lead here, as well as the
111 | # generic None value it's not yet been set
112 | if value == 'Custom' and self.text_color:
113 | self.color = self.text_color
114 | elif value == 'ContrastParentBackground' and self.parent_background:
115 | self.color = get_contrast_text_color(self.parent_background)
116 | else:
117 | self.color = [0, 0, 0, 1]
118 |
119 | def on_text_color(self, *args):
120 | if self.theme_text_color == 'Custom':
121 | self.color = self.text_color
122 |
123 | def on_opposite_colors(self, instance, value):
124 | self.on_theme_text_color(self, self.theme_text_color)
125 |
126 |
127 | class MDIcon(MDLabel):
128 | icon = StringProperty('android')
129 |
--------------------------------------------------------------------------------
/kivymd/material_resources.py:
--------------------------------------------------------------------------------
1 | """
2 | Material Resources
3 | ==================
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy import platform
18 | from kivy.core.window import Window
19 | from kivy.metrics import dp
20 |
21 | # Feel free to override this const if you're designing for a device such as
22 | # a GNU/Linux tablet.
23 | DEVICE_IOS = platform == "ios" or platform == "macosx"
24 | if platform != "android" and platform != "ios":
25 | DEVICE_TYPE = "desktop"
26 | elif Window.width >= dp(600) and Window.height >= dp(600):
27 | DEVICE_TYPE = "tablet"
28 | else:
29 | DEVICE_TYPE = "mobile"
30 |
31 | if DEVICE_TYPE == "mobile":
32 | MAX_NAV_DRAWER_WIDTH = dp(300)
33 | HORIZ_MARGINS = dp(16)
34 | STANDARD_INCREMENT = dp(56)
35 | PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
36 | LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT - dp(8)
37 | else:
38 | MAX_NAV_DRAWER_WIDTH = dp(400)
39 | HORIZ_MARGINS = dp(24)
40 | STANDARD_INCREMENT = dp(64)
41 | PORTRAIT_TOOLBAR_HEIGHT = STANDARD_INCREMENT
42 | LANDSCAPE_TOOLBAR_HEIGHT = STANDARD_INCREMENT
43 |
44 | TOUCH_TARGET_HEIGHT = dp(48)
45 |
--------------------------------------------------------------------------------
/kivymd/menu.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from kivy.animation import Animation
3 | from kivy.clock import Clock
4 | from kivy.core.window import Window
5 | from kivy.lang import Builder
6 | from recycleview import RecycleView
7 | from kivy.metrics import dp
8 | from kivy.properties import NumericProperty, ListProperty, OptionProperty, \
9 | StringProperty
10 | from kivy.uix.behaviors import ButtonBehavior
11 | from kivy.uix.boxlayout import BoxLayout
12 | import kivymd.material_resources as m_res
13 | from kivymd.theming import ThemableBehavior
14 |
15 | Builder.load_string('''
16 | #:import STD_INC kivymd.material_resources.STANDARD_INCREMENT
17 |
18 | size_hint_y: None
19 | height: dp(48)
20 | padding: dp(16), 0
21 | on_release: root.parent.parent.parent.parent.dismiss() # Horrible, but hey it works
22 | MDLabel:
23 | text: root.text
24 | theme_text_color: 'Primary'
25 |
26 |
27 | size_hint: None, None
28 | width: root.width_mult * STD_INC
29 | key_viewclass: 'viewclass'
30 | key_size: 'height'
31 |
32 |
33 | FloatLayout:
34 | id: fl
35 | MDMenu:
36 | id: md_menu
37 | data: root.items
38 | width_mult: root.width_mult
39 | size_hint: None, None
40 | size: 0,0
41 | canvas.before:
42 | Color:
43 | rgba: root.theme_cls.bg_light
44 | Rectangle:
45 | size: self.size
46 | pos: self.pos
47 | ''')
48 |
49 |
50 | class MDMenuItem(ButtonBehavior, BoxLayout):
51 | text = StringProperty()
52 |
53 |
54 | class MDMenu(RecycleView):
55 | width_mult = NumericProperty(1)
56 |
57 |
58 | class MDDropdownMenu(ThemableBehavior, BoxLayout):
59 | items = ListProperty()
60 | '''See :attr:`~kivy.garden.recycleview.RecycleView.data`
61 | '''
62 |
63 | width_mult = NumericProperty(1)
64 | '''This number multiplied by the standard increment (56dp on mobile,
65 | 64dp on desktop, determines the width of the menu items.
66 |
67 | If the resulting number were to be too big for the application Window,
68 | the multiplier will be adjusted for the biggest possible one.
69 | '''
70 |
71 | max_height = NumericProperty()
72 | '''The menu will grow no bigger than this number.
73 |
74 | Set to 0 for no limit. Defaults to 0.
75 | '''
76 |
77 | border_margin = NumericProperty(dp(4))
78 | '''Margin between Window border and menu
79 | '''
80 |
81 | ver_growth = OptionProperty(None, allownone=True,
82 | options=['up', 'down'])
83 | '''Where the menu will grow vertically to when opening
84 |
85 | Set to None to let the widget pick for you. Defaults to None.
86 | '''
87 |
88 | hor_growth = OptionProperty(None, allownone=True,
89 | options=['left', 'right'])
90 | '''Where the menu will grow horizontally to when opening
91 |
92 | Set to None to let the widget pick for you. Defaults to None.
93 | '''
94 |
95 | def open(self, *largs):
96 | Window.add_widget(self)
97 | Clock.schedule_once(lambda x: self.display_menu(largs[0]), -1)
98 |
99 | def display_menu(self, caller):
100 | # We need to pick a starting point, see how big we need to be,
101 | # and where to grow to.
102 |
103 | c = caller.to_window(caller.center_x,
104 | caller.center_y) # Starting coords
105 |
106 | # ---ESTABLISH INITIAL TARGET SIZE ESTIMATE---
107 | target_width = self.width_mult * m_res.STANDARD_INCREMENT
108 | # If we're wider than the Window...
109 | if target_width > Window.width:
110 | # ...reduce our multiplier to max allowed.
111 | target_width = int(
112 | Window.width / m_res.STANDARD_INCREMENT) * m_res.STANDARD_INCREMENT
113 |
114 | target_height = sum([dp(48) for i in self.items])
115 | # If we're over max_height...
116 | if self.max_height > 0 and target_height > self.max_height:
117 | target_height = self.max_height
118 |
119 | # ---ESTABLISH VERTICAL GROWTH DIRECTION---
120 | if self.ver_growth is not None:
121 | ver_growth = self.ver_growth
122 | else:
123 | # If there's enough space below us:
124 | if target_height <= c[1] - self.border_margin:
125 | ver_growth = 'down'
126 | # if there's enough space above us:
127 | elif target_height < Window.height - c[1] - self.border_margin:
128 | ver_growth = 'up'
129 | # otherwise, let's pick the one with more space and adjust ourselves
130 | else:
131 | # if there's more space below us:
132 | if c[1] >= Window.height - c[1]:
133 | ver_growth = 'down'
134 | target_height = c[1] - self.border_margin
135 | # if there's more space above us:
136 | else:
137 | ver_growth = 'up'
138 | target_height = Window.height - c[1] - self.border_margin
139 |
140 | if self.hor_growth is not None:
141 | hor_growth = self.hor_growth
142 | else:
143 | # If there's enough space to the right:
144 | if target_width <= Window.width - c[0] - self.border_margin:
145 | hor_growth = 'right'
146 | # if there's enough space to the left:
147 | elif target_width < c[0] - self.border_margin:
148 | hor_growth = 'left'
149 | # otherwise, let's pick the one with more space and adjust ourselves
150 | else:
151 | # if there's more space to the right:
152 | if Window.width - c[0] >= c[0]:
153 | hor_growth = 'right'
154 | target_width = Window.width - c[0] - self.border_margin
155 | # if there's more space to the left:
156 | else:
157 | hor_growth = 'left'
158 | target_width = c[0] - self.border_margin
159 |
160 | if ver_growth == 'down':
161 | tar_y = c[1] - target_height
162 | else: # should always be 'up'
163 | tar_y = c[1]
164 |
165 | if hor_growth == 'right':
166 | tar_x = c[0]
167 | else: # should always be 'left'
168 | tar_x = c[0] - target_width
169 | anim = Animation(x=tar_x, y=tar_y,
170 | width=target_width, height=target_height,
171 | duration=.3, transition='out_quint')
172 | menu = self.ids['md_menu']
173 | menu.pos = c
174 | anim.start(menu)
175 |
176 | def on_touch_down(self, touch):
177 | if not self.ids['md_menu'].collide_point(*touch.pos):
178 | self.dismiss()
179 | return True
180 | super(MDDropdownMenu, self).on_touch_down(touch)
181 | return True
182 |
183 | def on_touch_move(self, touch):
184 | super(MDDropdownMenu, self).on_touch_move(touch)
185 | return True
186 |
187 | def on_touch_up(self, touch):
188 | super(MDDropdownMenu, self).on_touch_up(touch)
189 | return True
190 |
191 | def dismiss(self):
192 | Window.remove_widget(self)
193 |
--------------------------------------------------------------------------------
/kivymd/popupscreen.py:
--------------------------------------------------------------------------------
1 | """
2 | Popup Screen
3 | ============
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 |
13 | Example
14 | -------
15 |
16 | from kivy.app import App
17 | from kivy.lang import Builder
18 | from kivy.metrics import dp
19 | from kivy.uix.boxlayout import BoxLayout
20 |
21 | from kivymd.button import MDIconButton
22 | from kivymd.list import ILeftBodyTouch
23 | from kivymd.popupscreen import MDPopupScreen
24 | from kivymd.theming import ThemeManager
25 |
26 | Builder.load_string('''
27 | #:import get_hex_from_color kivy.utils.get_hex_from_color
28 | #:import MDToolbar kivymd.toolbar.MDToolbar
29 | #:import OneLineIconListItem kivymd.list.OneLineIconListItem
30 | #:import MDRoundFlatButton kivymd.button.MDRoundFlatButton
31 | #:import Window kivy.core.window.Window
32 |
33 |
34 | ###############################################################################
35 | #
36 | # EXAMPLE TO USE
37 | #
38 | ###############################################################################
39 |
40 |
41 |
42 | BoxLayout:
43 | orientation: 'vertical'
44 |
45 | MDToolbar:
46 | id: toolbar
47 | title: 'Example Popup Screen'
48 | md_bg_color: app.theme_cls.primary_color
49 | left_action_items: [['menu', lambda x: x]]
50 | background_palette: 'Primary'
51 |
52 | StartScreen:
53 | id: start_screen
54 |
55 | canvas.before:
56 | Color:
57 | rgba: 1, 1, 1, 1
58 | Rectangle:
59 | pos: self.pos
60 | size: self.size
61 |
62 | ###############################################################################
63 | #
64 | # YOUR ROOT SCREEN
65 | #
66 | ###############################################################################
67 |
68 |
69 | orientation: 'vertical'
70 | padding: dp(1)
71 | spacing: dp(30)
72 |
73 | Image:
74 | id: image
75 | source: 'demos/kitchen_sink/assets/tangerines-1111529_1280.jpg'
76 | size_hint: 1, None
77 | height: dp(Window.height * 35 // 100)
78 | allow_stretch: True
79 | keep_ratio: False
80 |
81 | MDRoundFlatButton:
82 | text: 'Open Menu'
83 | pos_hint: {'center_x': .5}
84 | on_release: root.parent.parent.show()
85 |
86 | Widget:
87 |
88 | ###############################################################################
89 | #
90 | # YOUR POPUP SCREEN
91 | #
92 | ###############################################################################
93 |
94 |
95 | orientation: 'vertical'
96 |
97 | BoxLayout:
98 | size_hint_y: None
99 | height: self.minimum_height
100 |
101 | Widget:
102 | MDRoundFlatButton:
103 | text: "Free call"
104 | Widget:
105 | MDRoundFlatButton:
106 | text: "Free message"
107 | Widget:
108 |
109 | OneLineIconListItem:
110 | text: "Video call"
111 | IconLeftSampleWidget:
112 | icon: 'camera-front-variant'
113 |
114 | TwoLineIconListItem:
115 | text: "Call Viber Out"
116 | secondary_text:
117 | "[color=%s]Advantageous rates for calls[/color]"\
118 | % get_hex_from_color(app.theme_cls.primary_color)
119 | IconLeftSampleWidget:
120 | icon: 'phone'
121 |
122 | TwoLineIconListItem:
123 | text: "Call over mobile network"
124 | secondary_text:
125 | "[color=%s]Operator's tariffs apply[/color]"\
126 | % get_hex_from_color(app.theme_cls.primary_color)
127 | IconLeftSampleWidget:
128 | icon: 'remote'
129 |
130 | Widget:
131 | ''')
132 |
133 |
134 | class PopupScreen(MDPopupScreen):
135 | pass
136 |
137 |
138 | class MyPopupScreen(BoxLayout):
139 | pass
140 |
141 |
142 | class StartScreen(BoxLayout):
143 | pass
144 |
145 |
146 | class IconLeftSampleWidget(ILeftBodyTouch, MDIconButton):
147 | pass
148 |
149 |
150 | class MyApp(App):
151 | theme_cls = ThemeManager()
152 | theme_cls.primary_palette = 'Red'
153 |
154 | def build(self):
155 | popup_screen = MyPopupScreen()
156 | root = PopupScreen(screen=popup_screen,
157 | background_color=[.3, .3, .3, 1])
158 | root.max_height = root.ids.start_screen.ids.image.height\
159 | + root.ids.toolbar.height + dp(5)
160 | return root
161 |
162 |
163 | MyApp().run()
164 | """
165 |
166 | from kivy.clock import Clock
167 | from kivy.metrics import dp
168 | from kivy.uix.boxlayout import BoxLayout
169 | from kivy.uix.floatlayout import FloatLayout
170 | from kivy.animation import Animation
171 | from kivy.lang import Builder
172 | from kivy.core.window import Window
173 | from kivy.properties import ObjectProperty, ListProperty
174 |
175 | from kivymd.theming import ThemableBehavior
176 |
177 | Builder.load_string('''
178 |
179 | padding: dp(15)
180 |
181 | canvas:
182 | Color:
183 | rgba:
184 | self.theme_cls.bg_light if not len(root.background_color)\
185 | else root.background_color
186 | RoundedRectangle:
187 | pos: self.pos
188 | size: self.size
189 | radius: [15, ]
190 | ''')
191 |
192 |
193 | class RootScreen(BoxLayout, ThemableBehavior):
194 | background_color = ListProperty()
195 |
196 |
197 | class MDPopupScreen(FloatLayout):
198 | screen = ObjectProperty()
199 | background_color = ListProperty()
200 | added_screen = False
201 | max_height = dp(100)
202 | open_menu = False
203 |
204 | def __init__(self, **kwargs):
205 | super().__init__(**kwargs)
206 | self.root_screen = RootScreen()
207 | self.root_screen.y = -Window.height
208 |
209 | def show(self):
210 | if not self.added_screen:
211 | self.root_screen.add_widget(self.screen)
212 | self.add_widget(self.root_screen)
213 | self.added_screen = True
214 | self.root_screen.background_color = self.background_color
215 | self.open_menu = True
216 | Animation(y=-self.max_height, d=.2, t='in_out_bounce').start(
217 | self.root_screen)
218 |
219 | def hide(self, interval):
220 | Animation(y=-Window.height, d=.2, t='in_out_bounce').start(
221 | self.root_screen)
222 | self.open_menu = False
223 |
224 | def on_touch_down(self, touch):
225 | if touch.button == 'scrollup' or touch.button == 'scrolldown':
226 | return
227 | if self.open_menu:
228 | Clock.schedule_once(self.hide, .3)
229 | return super().on_touch_down(touch)
230 |
--------------------------------------------------------------------------------
/kivymd/progressbar.py:
--------------------------------------------------------------------------------
1 | """
2 | Progress Bar
3 | ============
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.lang import Builder
18 | from kivy.properties import OptionProperty, BooleanProperty
19 |
20 | from kivymd.theming import ThemableBehavior
21 | from kivy.uix.progressbar import ProgressBar
22 |
23 | Builder.load_string('''
24 |
25 | canvas:
26 | Clear
27 | Color:
28 | rgba: self.theme_cls.divider_color
29 | Rectangle:
30 | size:
31 | (self.width , dp(4)) if self.orientation == 'horizontal'\
32 | else (dp(4),self.height)
33 | pos:
34 | (self.x, self.center_y - dp(4))\
35 | if self.orientation == 'horizontal'\
36 | else (self.center_x - dp(4),self.y)
37 | Color:
38 | rgba: self.theme_cls.primary_color
39 | Rectangle:
40 | size:
41 | (self.width * self.value_normalized, sp(4))\
42 | if self.orientation == 'horizontal' else (sp(4),\
43 | self.height*self.value_normalized)
44 | pos:
45 | (self.width*(1 - self.value_normalized) + self.x\
46 | if self.reversed else self.x, self.center_y - dp(4))\
47 | if self.orientation == 'horizontal'\
48 | else (self.center_x - dp(4),self.height\
49 | * (1 - self.value_normalized) + self.y if self.reversed\
50 | else self.y)
51 | ''')
52 |
53 |
54 | class MDProgressBar(ThemableBehavior, ProgressBar):
55 | reversed = BooleanProperty(False)
56 | ''' Reverse the direction the progressbar moves. '''
57 |
58 | orientation = OptionProperty('horizontal',
59 | options=['horizontal', 'vertical'])
60 | ''' Orientation of progressbar'''
61 |
62 |
63 | if __name__ == '__main__':
64 | from kivy.app import App
65 | from kivymd.theming import ThemeManager
66 |
67 | class ProgressBarApp(App):
68 | theme_cls = ThemeManager()
69 |
70 | def build(self):
71 | return Builder.load_string('''
72 | #:import MDSlider kivymd.slider.MDSlider
73 |
74 |
75 | BoxLayout:
76 | orientation:'vertical'
77 | padding: '8dp'
78 | MDSlider:
79 | id:slider
80 | min:0
81 | max:100
82 | value: 40
83 |
84 | MDProgressBar:
85 | value: slider.value
86 | MDProgressBar:
87 | reversed: True
88 | value: slider.value
89 | BoxLayout:
90 | MDProgressBar:
91 | orientation:"vertical"
92 | reversed: True
93 | value: slider.value
94 |
95 | MDProgressBar:
96 | orientation:"vertical"
97 | value: slider.value
98 | ''')
99 |
100 |
101 | ProgressBarApp().run()
102 |
--------------------------------------------------------------------------------
/kivymd/progressloader.py:
--------------------------------------------------------------------------------
1 | """
2 | Progress Loader
3 | ===============
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 |
13 | Progressbar downloads files from the server.
14 |
15 | Example
16 | -------
17 |
18 | import os
19 |
20 | from kivy.app import App
21 | from kivy.lang import Builder
22 | from kivy.factory import Factory
23 |
24 | from kivymd.progressloader import MDProgressLoader
25 | from kivymd.theming import ThemeManager
26 | from kivymd.toast import toast
27 |
28 |
29 | Builder.load_string('''
30 | #:import MDToolbar kivymd.toolbar.MDToolbar
31 | #:import MDRoundFlatIconButton kivymd.button.MDRoundFlatIconButton
32 |
33 |
34 |
35 | orientation: 'vertical'
36 | spacing: dp(5)
37 |
38 | MDToolbar:
39 | id: toolbar
40 | title: 'MD Progress Loader'
41 | left_action_items: [['menu', lambda x: None]]
42 | elevation: 10
43 | md_bg_color: app.theme_cls.primary_color
44 |
45 | FloatLayout:
46 | id: box
47 |
48 | MDRoundFlatIconButton:
49 | text: "Download file"
50 | icon: "download"
51 | pos_hint: {'center_x': .5, 'center_y': .6}
52 | on_release: app.show_example_download_file()
53 | ''')
54 |
55 |
56 | class Test(App):
57 | theme_cls = ThemeManager()
58 |
59 | def __init__(self, **kwargs):
60 | super().__init__(**kwargs)
61 |
62 | def build(self):
63 | self.main_widget = Factory.Root()
64 | return self.main_widget
65 |
66 | def set_chevron_back_screen(self):
67 | '''Sets the return chevron to the previous screen in ToolBar.'''
68 |
69 | self.main_widget.ids.toolbar.right_action_items = []
70 |
71 | def download_progress_hide(self, instance_progress, value):
72 | '''Hides progress progress.'''
73 |
74 | self.main_widget.ids.toolbar.right_action_items =\
75 | [['download',
76 | lambda x: self.download_progress_show(instance_progress)]]
77 |
78 | def download_progress_show(self, instance_progress):
79 | self.set_chevron_back_screen()
80 | instance_progress.open()
81 | instance_progress.animation_progress_from_fade()
82 |
83 | def show_example_download_file(self):
84 | link = 'https://www.python.org/ftp/python/3.5.1/python-3.5.1-embed-win32.zip'
85 | progress = MDProgressLoader(
86 | url_on_image=link,
87 | path_to_file=os.path.join(self.directory, 'python-3.5.1.zip'),
88 | download_complete=self.download_complete,
89 | download_hide=self.download_progress_hide
90 | )
91 | progress.start(self.main_widget.ids.box)
92 |
93 | def download_complete(self):
94 | self.set_chevron_back_screen()
95 | toast('Done')
96 |
97 |
98 | Test().run()
99 | """
100 |
101 | from kivy.clock import Clock
102 | from kivy.core.window import Window
103 | from kivy.animation import Animation
104 | from kivy.network.urlrequest import UrlRequest
105 | from kivy.lang import Builder
106 | from kivy.properties import StringProperty, ObjectProperty, BooleanProperty
107 |
108 | from kivymd.cards import MDCard
109 |
110 | Builder.load_string('''
111 | #:import Window kivy.core.window.Window
112 | #:import MDSpinner kivymd.spinner.MDSpinner
113 | #:import MDLabel kivymd.label.MDLabel
114 | #:import MDCard kivymd.cards.MDCard
115 |
116 |
117 |
118 | pos: (Window.width // 2) - (self.width // 2), (Window.height // 2) - (self.height // 2)
119 | size_hint_y: None
120 | size_hint_x: .8
121 | height: spinner.height + dp(20)
122 | spacing: dp(10)
123 | padding: dp(10)
124 |
125 | canvas:
126 | Color:
127 | rgba: app.theme_cls.primary_color
128 | Rectangle:
129 | size: self.size
130 | pos: self.pos
131 |
132 | MDSpinner
133 | id: spinner
134 | size_hint: None, None
135 | size: dp(46), dp(46)
136 | color: 1, 1, 1, 1
137 |
138 | MDLabel:
139 | id: label_download
140 | shorten: True
141 | max_lines: 1
142 | halign: 'left'
143 | valign: 'top'
144 | text_size: self.width, None
145 | size_hint_y: None
146 | height: spinner.height
147 | size_hint_x: .8
148 | text: 'Download...'
149 |
150 | Widget:
151 | size_hint_x: .1
152 | ''')
153 |
154 |
155 | class MDProgressLoader(MDCard):
156 | path_to_file = StringProperty()
157 | '''The path to which the uploaded file will be saved.'''
158 |
159 | url_on_image = StringProperty()
160 | '''Link to uploaded file.'''
161 |
162 | label_download = StringProperty('Download')
163 | '''Signature of the downloaded file.'''
164 |
165 | download_complete = ObjectProperty()
166 | '''Function, called after a successful file upload.'''
167 |
168 | download_hide = ObjectProperty(lambda x: None)
169 | '''Function that is called when the download window is closed.'''
170 |
171 | download_flag = BooleanProperty(False)
172 | '''If True - the download process is in progress.'''
173 |
174 | def __init__(self, **kwargs):
175 | super().__init__(**kwargs)
176 | self.root_instance = None
177 |
178 | def start(self, root_instance):
179 | self.root_instance = root_instance
180 | self.download_flag = True
181 | self.root_instance.add_widget(self)
182 | self.retrieve_progress_load(self.url_on_image, self.path_to_file)
183 | Clock.schedule_once(self.animation_progress_to_fade, 2.5)
184 |
185 | def open(self):
186 | self.animation_progress_from_fade()
187 |
188 | def draw_progress(self, percent):
189 | """
190 | :type percent: int;
191 | :param percent: loading percentage;
192 |
193 | """
194 |
195 | self.ids.label_download.text = '%s: %d %%'\
196 | % (self.label_download, percent)
197 |
198 | def animation_progress_to_fade(self, interval):
199 | if not self.download_flag:
200 | return
201 |
202 | animation = Animation(
203 | center_y=Window.height, center_x=Window.width,
204 | opacity=0, d=.2, t='out_quad'
205 | )
206 | animation.bind(on_complete=lambda x, y: self.download_hide(self, None))
207 | animation.start(self)
208 |
209 | def animation_progress_from_fade(self):
210 | animation = Animation(
211 | center_y=Window.height // 2, center_x=Window.width // 2,
212 | opacity=1, d=.2, t='out_quad'
213 | )
214 | animation.start(self)
215 | Clock.schedule_once(self.animation_progress_to_fade, 2.5)
216 |
217 | def retrieve_progress_load(self, url, path):
218 | """
219 | :type url: str;
220 | :param url: link to content;
221 |
222 | :type path: str;
223 | :param path: path to save content;
224 | """
225 |
226 | req = UrlRequest(
227 | url, on_progress=self.update_progress, chunk_size=1024,
228 | on_success=self.on_success, file_path=path)
229 |
230 | def update_progress(self, request, current_size, total_size):
231 | percent = current_size * 100 // total_size
232 | self.draw_progress(percent)
233 |
234 | def on_success(self, req, result):
235 | self.root_instance.remove_widget(self)
236 | self.download_complete()
237 | self.download_flag = False
238 |
--------------------------------------------------------------------------------
/kivymd/refreshlayout.py:
--------------------------------------------------------------------------------
1 | """
2 | ScrollView Refresh Layout
3 | =========================
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 |
13 | Example
14 | -------
15 |
16 | from kivy.app import App
17 | from kivy.clock import Clock
18 | from kivy.lang import Builder
19 | from kivy.factory import Factory
20 | from kivy.properties import StringProperty
21 |
22 | from kivymd.button import MDIconButton
23 | from kivymd.icon_definitions import md_icons
24 | from kivymd.list import ILeftBodyTouch, OneLineIconListItem
25 | from kivymd.theming import ThemeManager
26 | from kivymd.utils import asynckivy
27 |
28 | Builder.load_string('''
29 | #:import MDToolbar kivymd.toolbar.MDToolbar
30 | #:import MDScrollViewRefreshLayout kivymd.refreshlayout.MDScrollViewRefreshLayout
31 |
32 |
33 |
34 | text: root.text
35 |
36 | IconLeftSampleWidget:
37 | icon: root.icon
38 |
39 |
40 |
41 |
42 | BoxLayout:
43 | orientation: 'vertical'
44 |
45 | MDToolbar:
46 | title: app.title
47 | md_bg_color: app.theme_cls.primary_color
48 | background_palette: 'Primary'
49 | elevation: 10
50 | left_action_items: [['menu', lambda x: x]]
51 |
52 | MDScrollViewRefreshLayout:
53 | id: refresh_layout
54 | refresh_callback: app.refresh_callback
55 | root_layout: root
56 |
57 | GridLayout:
58 | id: box
59 | size_hint_y: None
60 | height: self.minimum_height
61 | cols: 1
62 | ''')
63 |
64 |
65 | class IconLeftSampleWidget(ILeftBodyTouch, MDIconButton):
66 | pass
67 |
68 |
69 | class ItemForList(OneLineIconListItem):
70 | icon = StringProperty()
71 |
72 |
73 | class Example(App):
74 | title = 'Example Refresh Layout'
75 | theme_cls = ThemeManager()
76 | screen = None
77 | x = 0
78 | y = 15
79 |
80 | def build(self):
81 | self.screen = Factory.Example()
82 | self.set_list()
83 |
84 | return self.screen
85 |
86 | def set_list(self):
87 | async def set_list():
88 | names_icons_list = list(md_icons.keys())[self.x:self.y]
89 | for name_icon in names_icons_list:
90 | await asynckivy.sleep(0)
91 | self.screen.ids.box.add_widget(
92 | ItemForList(icon=name_icon, text=name_icon))
93 | asynckivy.start(set_list())
94 |
95 | def refresh_callback(self, *args):
96 | '''A method that updates the state of your application
97 | while the spinner remains on the screen.'''
98 |
99 | def refresh_callback(interval):
100 | self.screen.ids.box.clear_widgets()
101 | if self.x == 0:
102 | self.x, self.y = 15, 30
103 | else:
104 | self.x, self.y = 0, 15
105 | self.set_list()
106 | self.screen.ids.refresh_layout.refresh_done()
107 | self.tick = 0
108 |
109 | Clock.schedule_once(refresh_callback, 1)
110 |
111 |
112 | Example().run()
113 |
114 | """
115 |
116 | from kivy.animation import Animation
117 | from kivy.effects.dampedscroll import DampedScrollEffect
118 | from kivy.lang import Builder
119 | from kivy.metrics import dp
120 | from kivy.properties import NumericProperty, ObjectProperty, ListProperty
121 | from kivy.uix.floatlayout import FloatLayout
122 | from kivy.uix.scrollview import ScrollView
123 | from kivy.core.window import Window
124 |
125 | from kivymd.theming import ThemableBehavior
126 |
127 | Builder.load_string("""
128 | #:import Window kivy.core.window.Window
129 | #:import MDSpinner kivymd.spinner.MDSpinner
130 |
131 |
132 |
133 |
134 | AnchorLayout:
135 | id: body_spinner
136 | size_hint: None, None
137 | size: dp(46), dp(46)
138 | y: Window.height
139 | pos_hint: {'center_x': .5}
140 | anchor_x: 'center'
141 | anchor_y: 'center'
142 |
143 | canvas:
144 | Clear
145 | Color:
146 | rgba: root.theme_cls.primary_dark
147 | Ellipse:
148 | pos: self.pos
149 | size: self.size
150 |
151 | MDSpinner:
152 | id: spinner
153 | size_hint: None, None
154 | size: dp(30), dp(30)
155 | color: 1, 1, 1, 1
156 | """)
157 |
158 |
159 | class _RefreshScrollEffect(DampedScrollEffect):
160 | """This class is simply based on DampedScrollEffect.
161 | If you need any documentation please look at kivy.effects.dampedscrolleffect.
162 | """
163 |
164 | min_scroll_to_reload = NumericProperty(-dp(100))
165 | '''Minimum overscroll value to reload.'''
166 |
167 | def on_overscroll(self, scrollview, overscroll):
168 | if overscroll < self.min_scroll_to_reload:
169 | scroll_view = self.target_widget.parent
170 | scroll_view._did_overscroll = True
171 | return True
172 | else:
173 | return False
174 |
175 |
176 | class MDScrollViewRefreshLayout(ScrollView):
177 | root_layout = ObjectProperty()
178 | '''The spinner will be attached to this layout.'''
179 |
180 | def __init__(self, **kargs):
181 | super().__init__(**kargs)
182 | self.effect_cls = _RefreshScrollEffect
183 | self._work_spinnrer = False
184 | self._did_overscroll = False
185 | self.refresh_spinner = None
186 |
187 | def on_touch_up(self, *args):
188 | if self._did_overscroll and not self._work_spinnrer:
189 | if self.refresh_callback:
190 | self.refresh_callback()
191 | if not self.refresh_spinner:
192 | self.refresh_spinner = RefreshSpinner(_refresh_layout=self)
193 | self.root_layout.add_widget(self.refresh_spinner)
194 | self.refresh_spinner.start_anim_spinner()
195 | self._work_spinnrer = True
196 | self._did_overscroll = False
197 | return True
198 |
199 | return super().on_touch_up(*args)
200 |
201 | def refresh_done(self):
202 | if self.refresh_spinner:
203 | self.refresh_spinner.hide_anim_spinner()
204 |
205 |
206 | class RefreshSpinner(ThemableBehavior, FloatLayout):
207 | spinner_color = ListProperty([1, 1, 1, 1])
208 |
209 | _refresh_layout = ObjectProperty()
210 | '''kivymd.refreshlayout.MDScrollViewRefreshLayout object.'''
211 |
212 | def start_anim_spinner(self):
213 | spinner = self.ids.body_spinner
214 | Animation(y=spinner.y - dp(76), d=.8, t='out_elastic').start(spinner)
215 |
216 | def hide_anim_spinner(self):
217 | spinner = self.ids.body_spinner
218 | anim = Animation(y=Window.height, d=.8, t='out_elastic')
219 | anim.bind(on_complete=self.set_spinner)
220 | anim.start(spinner)
221 |
222 | def set_spinner(self, *args):
223 | body_spinner = self.ids.body_spinner
224 | body_spinner.size = (dp(46), dp(46))
225 | body_spinner.y = Window.height
226 | body_spinner.opacity = 1
227 | spinner = self.ids.spinner
228 | spinner.size = (dp(30), dp(30))
229 | spinner.opacity = 1
230 | self._refresh_layout._work_spinnrer = False
231 | self._refresh_layout._did_overscroll = False
232 |
--------------------------------------------------------------------------------
/kivymd/ripplebehavior.py:
--------------------------------------------------------------------------------
1 | """
2 | Ripple Behavior
3 | ===============
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.properties import ListProperty, NumericProperty, StringProperty,\
18 | BooleanProperty
19 | from kivy.animation import Animation
20 | from kivy.graphics import Color, Ellipse, StencilPush, StencilPop,\
21 | StencilUse, StencilUnUse, Rectangle
22 |
23 |
24 | class CommonRipple(object):
25 | ripple_rad = NumericProperty()
26 | ripple_rad_default = NumericProperty(1)
27 | ripple_post = ListProperty()
28 | ripple_color = ListProperty()
29 | ripple_alpha = NumericProperty(.5)
30 | ripple_scale = NumericProperty(None)
31 | ripple_duration_in_fast = NumericProperty(.3)
32 | # FIXME: These speeds should be calculated based on widget size in dp
33 | ripple_duration_in_slow = NumericProperty(2)
34 | ripple_duration_out = NumericProperty(.3)
35 | ripple_func_in = StringProperty('out_quad')
36 | ripple_func_out = StringProperty('out_quad')
37 |
38 | doing_ripple = BooleanProperty(False)
39 | finishing_ripple = BooleanProperty(False)
40 | fading_out = BooleanProperty(False)
41 | _no_ripple_effect = BooleanProperty(False)
42 |
43 | def on_touch_down(self, touch):
44 | if touch.is_mouse_scrolling:
45 | return False
46 | if not self.collide_point(touch.x, touch.y):
47 | return False
48 |
49 | if not self.disabled:
50 | if self.doing_ripple:
51 | Animation.cancel_all(self, 'ripple_rad', 'ripple_color',
52 | 'rect_color')
53 | self.anim_complete()
54 | self.ripple_rad = self.ripple_rad_default
55 | self.ripple_pos = (touch.x, touch.y)
56 |
57 | if self.ripple_color:
58 | pass
59 | elif hasattr(self, 'theme_cls'):
60 | self.ripple_color = self.theme_cls.ripple_color
61 | else:
62 | # If no theme, set Gray 300
63 | self.ripple_color = [.8784313725490196, .8784313725490196,
64 | .8784313725490196, self.ripple_alpha]
65 | self.ripple_color[3] = self.ripple_alpha
66 |
67 | self.lay_canvas_instructions()
68 | self.finish_rad = max(self.width, self.height) * self.ripple_scale
69 | self.start_ripple()
70 | return super().on_touch_down(touch)
71 |
72 | def lay_canvas_instructions(self):
73 | raise NotImplementedError
74 |
75 | def on_touch_move(self, touch, *args):
76 | if not self.collide_point(touch.x, touch.y):
77 | if not self.finishing_ripple and self.doing_ripple:
78 | self.finish_ripple()
79 | return super().on_touch_move(touch, *args)
80 |
81 | def on_touch_up(self, touch):
82 | if self.collide_point(touch.x, touch.y) and self.doing_ripple:
83 | self.finish_ripple()
84 | return super().on_touch_up(touch)
85 |
86 | def start_ripple(self):
87 | if not self.doing_ripple:
88 | anim = Animation(
89 | ripple_rad=self.finish_rad,
90 | t='linear',
91 | duration=self.ripple_duration_in_slow)
92 | anim.bind(on_complete=self.fade_out)
93 | self.doing_ripple = True
94 | anim.start(self)
95 |
96 | def _set_ellipse(self, instance, value):
97 | self.ellipse.size = (self.ripple_rad, self.ripple_rad)
98 |
99 | # Adjust ellipse pos here
100 |
101 | def _set_color(self, instance, value):
102 | self.col_instruction.a = value[3]
103 |
104 | def finish_ripple(self):
105 | if self.doing_ripple and not self.finishing_ripple:
106 | Animation.cancel_all(self, 'ripple_rad')
107 | anim = Animation(ripple_rad=self.finish_rad,
108 | t=self.ripple_func_in,
109 | duration=self.ripple_duration_in_fast)
110 | anim.bind(on_complete=self.fade_out)
111 | self.finishing_ripple = True
112 | anim.start(self)
113 |
114 | def fade_out(self, *args):
115 | rc = self.ripple_color
116 | if not self.fading_out:
117 | Animation.cancel_all(self, 'ripple_color')
118 | anim = Animation(ripple_color=[rc[0], rc[1], rc[2], .0],
119 | t=self.ripple_func_out,
120 | duration=self.ripple_duration_out)
121 | anim.bind(on_complete=self.anim_complete)
122 | self.fading_out = True
123 | anim.start(self)
124 |
125 | def anim_complete(self, *args):
126 | self.doing_ripple = False
127 | self.finishing_ripple = False
128 | self.fading_out = False
129 | self.canvas.after.clear()
130 |
131 |
132 | class RectangularRippleBehavior(CommonRipple):
133 | ripple_scale = NumericProperty(2.75)
134 |
135 | def lay_canvas_instructions(self):
136 | if self._no_ripple_effect:
137 | return
138 | with self.canvas.after:
139 | StencilPush()
140 | Rectangle(pos=self.pos, size=self.size)
141 | StencilUse()
142 | self.col_instruction = Color(rgba=self.ripple_color)
143 | self.ellipse =\
144 | Ellipse(size=(self.ripple_rad, self.ripple_rad),
145 | pos=(self.ripple_pos[0] - self.ripple_rad / 2.,
146 | self.ripple_pos[1] - self.ripple_rad / 2.))
147 | StencilUnUse()
148 | Rectangle(pos=self.pos, size=self.size)
149 | StencilPop()
150 | self.bind(ripple_color=self._set_color,
151 | ripple_rad=self._set_ellipse)
152 |
153 | def _set_ellipse(self, instance, value):
154 | super()._set_ellipse(instance, value)
155 | self.ellipse.pos = (self.ripple_pos[0] - self.ripple_rad / 2.,
156 | self.ripple_pos[1] - self.ripple_rad / 2.)
157 |
158 |
159 | class CircularRippleBehavior(CommonRipple):
160 | ripple_scale = NumericProperty(1)
161 |
162 | def lay_canvas_instructions(self):
163 | with self.canvas.after:
164 | StencilPush()
165 | self.stencil = Ellipse(size=(self.width * self.ripple_scale,
166 | self.height * self.ripple_scale),
167 | pos=(self.center_x - (
168 | self.width * self.ripple_scale) / 2,
169 | self.center_y - (
170 | self.height * self.ripple_scale) / 2))
171 | StencilUse()
172 | self.col_instruction = Color(rgba=self.ripple_color)
173 | self.ellipse = Ellipse(size=(self.ripple_rad, self.ripple_rad),
174 | pos=(self.center_x - self.ripple_rad / 2.,
175 | self.center_y - self.ripple_rad / 2.))
176 | StencilUnUse()
177 | Ellipse(pos=self.pos, size=self.size)
178 | StencilPop()
179 | self.bind(ripple_color=self._set_color,
180 | ripple_rad=self._set_ellipse)
181 |
182 | def _set_ellipse(self, instance, value):
183 | super()._set_ellipse(instance, value)
184 | if self.ellipse.size[0] > self.width * .6 and not self.fading_out:
185 | self.fade_out()
186 | self.ellipse.pos = (self.center_x - self.ripple_rad / 2.,
187 | self.center_y - self.ripple_rad / 2.)
188 |
--------------------------------------------------------------------------------
/kivymd/setup.py:
--------------------------------------------------------------------------------
1 | import re
2 | from distutils.core import setup
3 |
4 | VERSION_FILE = "kivymd/__init__.py"
5 | ver_file_data = open(VERSION_FILE, "rt").read()
6 | ver_regex = r"^__version__ = ['\"]([^'\"]*)['\"]"
7 | ver_reg_search = re.search(ver_regex, ver_file_data, re.M)
8 |
9 | if ver_reg_search:
10 | version = ver_reg_search.group(1)
11 | else:
12 | raise ValueError(
13 | "Unable to find version string in {}.".format(VERSION_FILE))
14 |
15 | setup(name='kivymd',
16 | version=version,
17 | description='Set of widgets for Kivy inspired by Google\'s Material '
18 | 'Design',
19 | author='Andrés Rodríguez, author fork - HeaTTheatR',
20 | author_email='andres.rodriguez@lithersoft.com, email author fork '
21 | '- kivydevelopment@gmail.com',
22 | url='https://github.com/HeaTTheatR/KivyMD',
23 | packages=['kivymd'],
24 | package_data={
25 | 'kivymd': ['images/*.png', 'images/*.jpg', 'images/*.atlas',
26 | 'fonts/*.ttf',
27 | 'vendor/*.py', 'vendor/circleLayout/*.py',
28 | 'vendor/circularTimePicker/*.py',
29 | 'vendor/navigationdrawer/*.py',
30 | 'toast/*.py', 'toast/kivytoast/*.py',
31 | 'toast/androidtoast/*.py', 'stiffscroll/*.py',
32 | 'utils/*.py']},
33 | requires=['kivy', 'pillow'])
34 |
--------------------------------------------------------------------------------
/kivymd/slidingpanel.py:
--------------------------------------------------------------------------------
1 | """
2 | Sliding Panel
3 | =============
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.animation import Animation
18 | from kivy.clock import Clock
19 | from kivy.core.window import Window
20 | from kivy.lang import Builder
21 | from kivy.properties import OptionProperty, NumericProperty, StringProperty,\
22 | ListProperty
23 | from kivy.uix.boxlayout import BoxLayout
24 |
25 | Builder.load_string('''
26 | #:import Window kivy.core.window.Window
27 |
28 |
29 |
30 | orientation: 'vertical'
31 | size_hint_x: None
32 | width: dp(320)
33 | x: -1 * self.width if self.side == 'left' else Window.width
34 |
35 |
36 |
37 | canvas:
38 | Color:
39 | rgba: root.color
40 | Rectangle:
41 | size: root.size
42 | ''')
43 |
44 |
45 | class PanelShadow(BoxLayout):
46 | color = ListProperty([0, 0, 0, 0])
47 |
48 |
49 | class SlidingPanel(BoxLayout):
50 | anim_length_close = NumericProperty(.3)
51 | anim_length_open = NumericProperty(.3)
52 | animation_t_open = StringProperty('out_sine')
53 | animation_t_close = StringProperty('out_sine')
54 | side = OptionProperty('left', options=['left', 'right'])
55 |
56 | _open = False
57 |
58 | def __init__(self, **kwargs):
59 | super().__init__(**kwargs)
60 | self.shadow = PanelShadow()
61 | Clock.schedule_once(lambda x: Window.add_widget(self.shadow, 89), 0)
62 | Clock.schedule_once(lambda x: Window.add_widget(self, 90), 0)
63 |
64 | def toggle(self):
65 | Animation.stop_all(self, 'x')
66 | Animation.stop_all(self.shadow, 'color')
67 | if self._open:
68 | if self.side == 'left':
69 | target_x = -1 * self.width
70 | else:
71 | target_x = Window.width
72 |
73 | sh_anim = Animation(duration=self.anim_length_open,
74 | t=self.animation_t_open,
75 | color=[0, 0, 0, 0])
76 | sh_anim.start(self.shadow)
77 | self._get_main_animation(duration=self.anim_length_close,
78 | t=self.animation_t_close,
79 | x=target_x,
80 | is_closing=True).start(self)
81 | self._open = False
82 | else:
83 | if self.side == 'left':
84 | target_x = 0
85 | else:
86 | target_x = Window.width - self.width
87 | Animation(duration=self.anim_length_open, t=self.animation_t_open,
88 | color=[0, 0, 0, .5]).start(self.shadow)
89 | self._get_main_animation(duration=self.anim_length_open,
90 | t=self.animation_t_open,
91 | x=target_x,
92 | is_closing=False).start(self)
93 | self._open = True
94 |
95 | def _get_main_animation(self, duration, t, x, is_closing):
96 | return Animation(duration=duration, t=t, x=x)
97 |
98 | def on_touch_down(self, touch):
99 | # Prevents touch events from propagating to anything below the widget.
100 | super().on_touch_down(touch)
101 | if self.collide_point(*touch.pos) or self._open:
102 | return True
103 |
104 | def on_touch_up(self, touch):
105 | super().on_touch_up(touch)
106 | if not self.collide_point(touch.x, touch.y) and self._open:
107 | self.toggle()
108 | return True
109 |
--------------------------------------------------------------------------------
/kivymd/snackbar.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | '''
3 | Snackbar
4 | ========
5 |
6 | `Material Design spec page `_
7 |
8 | API
9 | ---
10 | '''
11 |
12 | from collections import deque
13 | from kivy.animation import Animation
14 | from kivy.clock import Clock
15 | from kivy.core.window import Window
16 | from kivy.event import EventDispatcher
17 | from kivy.lang import Builder
18 | from kivy.metrics import dp
19 | from kivy.properties import ObjectProperty, StringProperty, NumericProperty
20 | from kivy.uix.relativelayout import RelativeLayout
21 | from kivymd.material_resources import DEVICE_TYPE
22 |
23 | Builder.load_string('''
24 | #:import Window kivy.core.window.Window
25 | #:import get_color_from_hex kivy.utils.get_color_from_hex
26 | #:import MDFlatButton kivymd.button.MDFlatButton
27 | #:import MDLabel kivymd.label.MDLabel
28 | #:import DEVICE_TYPE kivymd.material_resources.DEVICE_TYPE
29 | <_SnackbarWidget>
30 | canvas:
31 | Color:
32 | rgb: get_color_from_hex('323232')
33 | Rectangle:
34 | size: self.size
35 | size_hint_y: None
36 | size_hint_x: 1 if DEVICE_TYPE == 'mobile' else None
37 | height: dp(48) if _label.texture_size[1] < dp(30) else dp(80)
38 | width: dp(24) + _label.width + _spacer.width + root.padding_right if root.button_text == '' else dp(24) + \
39 | _label.width + _spacer.width + _button.width + root.padding_right
40 | top: 0
41 | x: 0 if DEVICE_TYPE == 'mobile' else Window.width/2 - self.width/2
42 | BoxLayout:
43 | width: Window.width - root.padding_right - _spacer.width - dp(24) if DEVICE_TYPE == 'mobile' and \
44 | root.button_text == '' else Window.width - root.padding_right - _button.width - _spacer.width - dp(24) \
45 | if DEVICE_TYPE == 'mobile' else _label.texture_size[0] if (dp(568) - root.padding_right - _button.width - \
46 | _spacer.width - _label.texture_size[0] - dp(24)) >= 0 else (dp(568) - root.padding_right - _button.width - \
47 | _spacer.width - dp(24))
48 | size_hint_x: None
49 | x: dp(24)
50 | MDLabel:
51 | id: _label
52 | text: root.text
53 | theme_text_color: 'Custom'
54 | text_color: get_color_from_hex('ffffff')
55 | size: self.texture_size
56 | BoxLayout:
57 | id: _spacer
58 | size_hint_x: None
59 | x: _label.right
60 | width: 0
61 | MDFlatButton:
62 | id: _button
63 | text: root.button_text
64 | theme_text_color: 'Custom'
65 | text_color: get_color_from_hex('ffffff')
66 | size_hint_x: None
67 | x: _spacer.right if root.button_text != '' else root.right
68 | center_y: root.height/2
69 | on_release: root.button_callback()
70 | ''')
71 |
72 |
73 | class SnackbarManager:
74 | playing = False
75 | queue = deque()
76 |
77 | def _play_next(self, dying_widget=None):
78 | if (dying_widget or not self.playing) and len(self.queue) > 0:
79 | self.playing = True
80 | self.queue.popleft().begin()
81 | elif len(self.queue) == 0:
82 | self.playing = False
83 |
84 | def make(self, text, button_text=None, button_callback=None, duration=3):
85 | if button_text is not None and button_callback is not None:
86 | self.queue.append(_SnackbarWidget(text=text, button_text=button_text, button_callback=button_callback,
87 | duration=duration))
88 | else:
89 | self.queue.append(_SnackbarWidget(text=text, duration=duration))
90 | self._play_next()
91 |
92 |
93 | class Snackbar(EventDispatcher):
94 | '''
95 | A Material Design Snackbar
96 | '''
97 | text = StringProperty("")
98 | '''The text that will appear in the Snackbar.
99 |
100 | :attr:`text` is a :class:`~kivy.properties.StringProperty` and defaults to ''.
101 | '''
102 | button_text = StringProperty(None, allownone=True)
103 | '''The text that will appear in the Snackbar's button.
104 |
105 | .. note::
106 | If this variable is None, the Snackbar will have no button.
107 |
108 | :attr:`button_text` is a :class:`~kivy.properties.StringProperty` and defaults to None.
109 | '''
110 | button_callback = ObjectProperty(None, allownone=True)
111 | '''The callback that will be triggered when the Snackbar's button is pressed.
112 |
113 | .. note::
114 | If this variable is None, the Snackbar will have no button.
115 |
116 | :attr:`button_callback` is a :class:`~kivy.properties.ObjectProperty` and defaults to None.
117 | '''
118 | duration = NumericProperty(3)
119 | '''The amount of time that the Snackbar will stay on screen for.
120 |
121 | :attr:`duration` is a :class:`~kivy.properties.NumericProperty` and defaults to 3.
122 | '''
123 |
124 | def __init__(self, text, button_text=None, button_callback=None, duration=None):
125 | self.text = text
126 | self.button_text = button_text
127 | self.button_callback = button_callback
128 | self.duration = duration or self.duration
129 |
130 | def show(self):
131 | '''Show the Snackbar'''
132 | manager.make(text=self.text, button_text=self.button_text, button_callback=self.button_callback,
133 | duration=self.duration)
134 |
135 |
136 | class _SnackbarWidget(RelativeLayout):
137 | text = StringProperty()
138 | button_text = StringProperty()
139 | button_callback = ObjectProperty()
140 | duration = NumericProperty()
141 | padding_right = NumericProperty(dp(24))
142 |
143 | def __init__(self, text, duration, button_text='', button_callback=None,
144 | **kwargs):
145 | super(_SnackbarWidget, self).__init__(**kwargs)
146 | self.text = text
147 | self.button_text = button_text
148 | self.button_callback = button_callback
149 | self.duration = duration
150 | self.ids['_label'].text_size = (None, None)
151 |
152 | def begin(self):
153 | if self.button_text == '':
154 | self.remove_widget(self.ids['_button'])
155 | else:
156 | self.ids['_spacer'].width = dp(16) if DEVICE_TYPE == "mobile" else dp(40)
157 | self.padding_right = dp(16)
158 | Window.add_widget(self)
159 | anim = Animation(y=0, duration=.3, t='out_quad')
160 | anim.start(self)
161 | Clock.schedule_once(lambda dt: self.die(), self.duration)
162 |
163 | def die(self):
164 | anim = Animation(top=0, duration=.3, t='out_quad')
165 | anim.bind(on_complete=lambda *args: manager._play_next(self))
166 | anim.bind(on_complete=lambda *args: Window.remove_widget(self))
167 | anim.start(self)
168 |
169 | manager = SnackbarManager()
170 |
--------------------------------------------------------------------------------
/kivymd/snackbars.py:
--------------------------------------------------------------------------------
1 | """
2 | Snackbars
3 | =========
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 |
13 | `Material Design spec, Snackbars `_
14 |
15 | Example
16 | =======
17 |
18 | from kivy.app import App
19 | from kivy.animation import Animation
20 | from kivy.clock import Clock
21 | from kivy.metrics import dp
22 | from kivy.lang import Builder
23 |
24 | from kivymd.snackbars import Snackbar
25 | from kivymd.theming import ThemeManager
26 | from kivymd.toast import toast
27 |
28 | KV = '''
29 | #:import Window kivy.core.window.Window
30 | #:import MDToolbar kivymd.toolbar.MDToolbar
31 | #:import MDRaisedButton kivymd.button.MDRaisedButton
32 | #:import MDSeparator kivymd.cards.MDSeparator
33 | #:import MDLabel kivymd.label.MDLabel
34 |
35 |
36 | Screen:
37 | name: 'snackbar'
38 |
39 | BoxLayout:
40 | orientation: 'vertical'
41 | spacing: dp(10)
42 |
43 | MDToolbar:
44 | title: 'Example Snackbar'
45 | md_bg_color: app.theme_cls.primary_color
46 | left_action_items: [['menu', lambda x: x]]
47 | background_palette: 'Primary'
48 |
49 | BoxLayout:
50 | orientation: 'vertical'
51 | spacing: dp(10)
52 | padding: dp(10)
53 |
54 | Widget:
55 |
56 | MDRaisedButton:
57 | text: "Create simple snackbar"
58 | pos_hint: {'center_x': .5}
59 | on_release: app.show_example_snackbar('simple')
60 |
61 | MDRaisedButton:
62 | text: "Create snackbar with button"
63 | pos_hint: {'center_x': .5}
64 | on_release: app.show_example_snackbar('button')
65 |
66 | MDRaisedButton:
67 | text: "Create snackbar with a lot of text"
68 | pos_hint: {'center_x': .5}
69 | on_release: app.show_example_snackbar('verylong')
70 |
71 | MDSeparator:
72 |
73 | MDLabel:
74 | text: 'Click the MDFloatingActionButton to show the following example...'
75 | halign: 'center'
76 |
77 | Widget:
78 |
79 | MDFloatingActionButton:
80 | id: button
81 | md_bg_color: app.theme_cls.primary_color
82 | x: Window.width - self.width - dp(10)
83 | y: dp(10)
84 | on_release: app.show_example_snackbar('float')
85 | '''
86 |
87 |
88 | class ExampleSnackBar(App):
89 | theme_cls = ThemeManager()
90 | _interval = 0
91 | my_snackbar = None
92 | screen = None
93 |
94 | def build(self):
95 | self.screen = Builder.load_string(KV)
96 | return self.screen
97 |
98 | def show_example_snackbar(self, snack_type):
99 | def callback(instance):
100 | toast(instance.text)
101 |
102 | def wait_interval(interval):
103 | self._interval += interval
104 | if self._interval > self.my_snackbar.duration:
105 | anim = Animation(y=dp(10), d=.2)
106 | anim.start(self.screen.ids.button)
107 | Clock.unschedule(wait_interval)
108 | self._interval = 0
109 | self.my_snackbar = None
110 |
111 | if snack_type == 'simple':
112 | Snackbar(text="This is a snackbar!").show()
113 | elif snack_type == 'button':
114 | Snackbar(text="This is a snackbar", button_text="with a button!",
115 | button_callback=callback).show()
116 | elif snack_type == 'verylong':
117 | Snackbar(text="This is a very very very very very very very "
118 | "long snackbar!").show()
119 | elif snack_type == 'float':
120 | if not self.my_snackbar:
121 | self.my_snackbar = Snackbar(
122 | text="This is a snackbar!", button_text='Button',
123 | duration=3, button_callback=callback)
124 | self.my_snackbar.show()
125 | anim = Animation(y=dp(72), d=.2)
126 | anim.bind(on_complete=lambda *args: Clock.schedule_interval(
127 | wait_interval, 0))
128 | anim.start(self.screen.ids.button)
129 |
130 |
131 | ExampleSnackBar().run()
132 | """
133 |
134 | from kivy.animation import Animation
135 | from kivy.clock import Clock
136 | from kivy.core.window import Window
137 | from kivy.lang import Builder
138 | from kivy.properties import ObjectProperty, StringProperty, NumericProperty
139 | from kivy.uix.floatlayout import FloatLayout
140 |
141 | from kivymd.button import MDFlatButton
142 |
143 | Builder.load_string('''
144 | #:import get_color_from_hex kivy.utils.get_color_from_hex
145 | #:import MDLabel kivymd.label.MDLabel
146 |
147 |
148 | :
149 |
150 | BoxLayout:
151 | id: box
152 | size_hint_y: None
153 | height: dp(58)
154 | spacing: dp(5)
155 | padding: dp(10)
156 | y: -self.height
157 |
158 | canvas:
159 | Color:
160 | rgba: get_color_from_hex('323232')
161 | Rectangle:
162 | pos: self.pos
163 | size: self.size
164 |
165 | MDLabel:
166 | id: text_bar
167 | size_hint_y: None
168 | height: self.texture_size[1]
169 | text: root.text
170 | font_size: root.font_size
171 | theme_text_color: 'Custom'
172 | text_color: get_color_from_hex('ffffff')
173 | shorten: True
174 | shorten_from: 'right'
175 | pos_hint: {'center_y': .5}
176 | ''')
177 |
178 |
179 | class Snackbar(FloatLayout):
180 | """A Material Design Snackbar"""
181 |
182 | text = StringProperty()
183 | """The text that will appear in the Snackbar.
184 |
185 | :attr:`text` is a :class:`~kivy.properties.StringProperty`
186 | and defaults to ''.
187 | """
188 |
189 | font_size = NumericProperty('15sp')
190 | """The font size of the text that will appear in the Snackbar.
191 |
192 | :attr:`font_size` is a :class:`~kivy.properties.NumericProperty` and
193 | defaults to 15sp.
194 | """
195 |
196 | button_text = StringProperty()
197 | """The text that will appear in the Snackbar's button.
198 |
199 | .. note::
200 | If this variable is None, the Snackbar will have no button.
201 |
202 | :attr:`button_text` is a :class:`~kivy.properties.StringProperty`
203 | and defaults to ''.
204 | """
205 |
206 | button_callback = ObjectProperty()
207 | """The callback that will be triggered when the Snackbar's
208 | button is pressed.
209 |
210 | .. note::
211 | If this variable is None, the Snackbar will have no button.
212 |
213 | :attr:`button_callback` is a :class:`~kivy.properties.ObjectProperty`
214 | and defaults to None.
215 | """
216 |
217 | duration = NumericProperty(3)
218 | """The amount of time that the Snackbar will stay on screen for.
219 |
220 | :attr:`duration` is a :class:`~kivy.properties.NumericProperty`
221 | and defaults to 3.
222 | """
223 |
224 | _interval = 0
225 |
226 | def __init__(self, **kwargs):
227 | super().__init__(**kwargs)
228 | if self.button_text != '':
229 | button = MDFlatButton(text=self.button_text)
230 | self.ids.box.add_widget(button)
231 | if self.button_callback:
232 | button.bind(on_release=self.button_callback)
233 |
234 | def show(self):
235 | """Show the Snackbar."""
236 |
237 | def wait_interval(interval):
238 | self._interval += interval
239 | if self._interval > self.duration:
240 | anim = Animation(y=-self.ids.box.height, d=.2)
241 | anim.bind(on_complete=lambda *args: Window.parent.remove_widget(self))
242 | anim.start(self.ids.box)
243 | Clock.unschedule(wait_interval)
244 | self._interval = 0
245 |
246 | Window.parent.add_widget(self)
247 | anim = Animation(y=0, d=.2)
248 | anim.bind(on_complete=lambda *args: Clock.schedule_interval(wait_interval, 0))
249 | anim.start(self.ids.box)
250 |
--------------------------------------------------------------------------------
/kivymd/spinner.py:
--------------------------------------------------------------------------------
1 | """
2 | Spinner
3 | =======
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 | """
16 |
17 | from kivy.lang import Builder
18 | from kivy.uix.widget import Widget
19 | from kivy.properties import NumericProperty, ListProperty, BooleanProperty
20 | from kivy.animation import Animation
21 | from kivymd.theming import ThemableBehavior
22 |
23 | Builder.load_string('''
24 |
25 | canvas.before:
26 | PushMatrix
27 | Rotate:
28 | angle: self._rotation_angle
29 | origin: self.center
30 | canvas:
31 | Color:
32 | rgba: self.color
33 | a: self._alpha
34 | SmoothLine:
35 | circle: self.center_x, self.center_y, self.width / 2,\
36 | self._angle_start, self._angle_end
37 | cap: 'square'
38 | width: dp(2.25)
39 | canvas.after:
40 | PopMatrix
41 |
42 | ''')
43 |
44 |
45 | class MDSpinner(ThemableBehavior, Widget):
46 | """:class:`MDSpinner` is an implementation of the circular progress
47 | indicator in Google's Material Design.
48 |
49 | It can be used either as an indeterminate indicator that loops while
50 | the user waits for something to happen, or as a determinate indicator.
51 |
52 | Set :attr:`determinate` to **True** to activate determinate mode, and
53 | :attr:`determinate_time` to set the duration of the animation.
54 | """
55 |
56 | determinate = BooleanProperty(False)
57 | """:attr:`determinate` is a :class:`~kivy.properties.BooleanProperty` and
58 | defaults to False
59 | """
60 |
61 | determinate_time = NumericProperty(2)
62 | """:attr:`determinate_time` is a :class:`~kivy.properties.NumericProperty`
63 | and defaults to 2
64 | """
65 |
66 | active = BooleanProperty(True)
67 | """Use :attr:`active` to start or stop the spinner.
68 |
69 | :attr:`active` is a :class:`~kivy.properties.BooleanProperty` and
70 | defaults to True
71 | """
72 |
73 | color = ListProperty([])
74 | """:attr:`color` is a :class:`~kivy.properties.ListProperty` and
75 | defaults to 'self.theme_cls.primary_color'
76 | """
77 |
78 | _alpha = NumericProperty(0)
79 | _rotation_angle = NumericProperty(360)
80 | _angle_start = NumericProperty(0)
81 | _angle_end = NumericProperty(8)
82 |
83 | def __init__(self, **kwargs):
84 | super().__init__(**kwargs)
85 | self.color = self.theme_cls.primary_color
86 | self._alpha_anim_in = Animation(_alpha=1, duration=.8, t='out_quad')
87 | self._alpha_anim_out = Animation(_alpha=0, duration=.3, t='out_quad')
88 | self._alpha_anim_out.bind(on_complete=self._reset)
89 | self.theme_cls.bind(primary_color=self._update_color)
90 |
91 | if self.determinate:
92 | self._start_determinate()
93 | else:
94 | self._start_loop()
95 |
96 | def _update_color(self, *args):
97 | self.color = self.theme_cls.primary_color
98 |
99 | def _start_determinate(self, *args):
100 | self._alpha_anim_in.start(self)
101 |
102 | _rot_anim = Animation(_rotation_angle=0,
103 | duration=self.determinate_time * .7,
104 | t='out_quad')
105 | _rot_anim.start(self)
106 |
107 | _angle_start_anim = Animation(_angle_end=360,
108 | duration=self.determinate_time,
109 | t='in_out_quad')
110 | _angle_start_anim.bind(
111 | on_complete=lambda *x: self._alpha_anim_out.start(self))
112 |
113 | _angle_start_anim.start(self)
114 |
115 | def _start_loop(self, *args):
116 | if self._alpha == 0:
117 | _rot_anim = Animation(_rotation_angle=0,
118 | duration=2,
119 | t='linear')
120 | _rot_anim.start(self)
121 |
122 | self._alpha = 1
123 | self._alpha_anim_in.start(self)
124 | _angle_start_anim = Animation(_angle_end=self._angle_end + 270,
125 | duration=.6,
126 | t='in_out_cubic')
127 | _angle_start_anim.bind(on_complete=self._anim_back)
128 | _angle_start_anim.start(self)
129 |
130 | def _anim_back(self, *args):
131 | _angle_back_anim = Animation(_angle_start=self._angle_end - 8,
132 | duration=.6,
133 | t='in_out_cubic')
134 | _angle_back_anim.bind(on_complete=self._start_loop)
135 |
136 | _angle_back_anim.start(self)
137 |
138 | def on__rotation_angle(self, *args):
139 | if self._rotation_angle == 0:
140 | self._rotation_angle = 360
141 | if not self.determinate:
142 | _rot_anim = Animation(_rotation_angle=0,
143 | duration=2)
144 | _rot_anim.start(self)
145 |
146 | def _reset(self, *args):
147 | Animation.cancel_all(self, '_angle_start', '_rotation_angle',
148 | '_angle_end', '_alpha')
149 | self._angle_start = 0
150 | self._angle_end = 8
151 | self._rotation_angle = 360
152 | self._alpha = 0
153 | self.active = False
154 |
155 | def on_active(self, *args):
156 | if not self.active:
157 | self._reset()
158 | else:
159 | if self.determinate:
160 | self._start_determinate()
161 | else:
162 | self._start_loop()
163 |
--------------------------------------------------------------------------------
/kivymd/stackfloatingbuttons.py:
--------------------------------------------------------------------------------
1 | """
2 | Stack Floating Buttons
3 | ======================
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 |
13 | Example
14 | -------
15 |
16 | from kivy.app import App
17 | from kivy.lang import Builder
18 | from kivy.factory import Factory
19 |
20 | from kivymd.toast import toast
21 | from kivymd.theming import ThemeManager
22 | from kivymd.stackfloatingbuttons import MDStackFloatingButtons
23 |
24 |
25 | Builder.load_string('''
26 | #:import MDToolbar kivymd.toolbar.MDToolbar
27 |
28 |
29 | :
30 | orientation: 'vertical'
31 |
32 | MDToolbar:
33 | title: 'Stack Floating Buttons'
34 | md_bg_color: app.theme_cls.primary_color
35 | elevation: 10
36 | left_action_items: [['menu', lambda x: None]]
37 |
38 | ''')
39 |
40 |
41 | class Example(App):
42 | theme_cls = ThemeManager()
43 | theme_cls.primary_palette = 'Teal'
44 | title = "Example Stack Floating Buttons"
45 | create_stack_floating_buttons = False
46 | floating_data = {
47 | 'Python': 'language-python',
48 | 'Php': 'language-php',
49 | 'C++': 'language-cpp'}
50 |
51 | def set_my_language(self, instance_button):
52 | toast(instance_button.icon)
53 |
54 | def build(self):
55 | screen = Factory.ExampleFloatingButtons()
56 | # Use this condition otherwise the stack will be created each time.
57 | if not self.create_stack_floating_buttons:
58 | screen.add_widget(MDStackFloatingButtons(
59 | icon='lead-pencil',
60 | floating_data=self.floating_data,
61 | callback=self.set_my_language))
62 | self.create_stack_floating_buttons = True
63 | return screen
64 |
65 |
66 | Example().run()
67 | """
68 |
69 | from kivy.animation import Animation
70 | from kivy.core.window import Window
71 | from kivy.uix.floatlayout import FloatLayout
72 | from kivy.lang import Builder
73 | from kivy.properties import StringProperty, DictProperty, ObjectProperty, ListProperty
74 | from kivy.metrics import dp
75 |
76 | from kivymd.cards import MDCard
77 |
78 |
79 | Builder.load_string('''
80 | #:import Window kivy.core.window.Window
81 | #:import MDFloatingActionButton kivymd.button.MDFloatingActionButton
82 |
83 |
84 |
85 | x: Window.width - (self.width + dp(21))
86 | y: dp(25)
87 | size_hint: None, None
88 | size: dp(46), dp(46)
89 | elevation: 5
90 | md_bg_color: app.theme_cls.primary_color
91 | text_color: root.parent.text_color
92 | on_release: self.parent.callback(self)
93 |
94 |
95 |
96 | size_hint: None, None
97 | height: dp(20)
98 | width: label.texture_size[0]
99 | border_color_a: .5
100 | md_bg_color: app.theme_cls.primary_color
101 | x: -self.width
102 |
103 | Label:
104 | id: label
105 | color: root.parent.text_color
106 | bold: True
107 | markup: True
108 | text: ' %s ' % root.text
109 |
110 |
111 |
112 | FloatingButton:
113 | id: f_btn_1
114 | icon: list(root.floating_data.values())[0]
115 | FloatingButton:
116 | id: f_btn_2
117 | icon: list(root.floating_data.values())[1]
118 | FloatingButton:
119 | id: f_btn_3
120 | icon: list(root.floating_data.values())[2]
121 |
122 | MDFloatingLabel:
123 | id: f_lbl_1
124 | text: list(root.floating_data.keys())[0]
125 | y: dp(117)
126 | MDFloatingLabel:
127 | id: f_lbl_2
128 | text: list(root.floating_data.keys())[1]
129 | y: dp(170)
130 | MDFloatingLabel:
131 | id: f_lbl_3
132 | text: list(root.floating_data.keys())[2]
133 | y: dp(226)
134 |
135 | MDFloatingActionButton:
136 | icon: root.icon
137 | size: dp(56), dp(56)
138 | x: Window.width - (self.width + dp(15))
139 | md_bg_color: app.theme_cls.primary_color
140 | text_color: root.text_color
141 | y: dp(15)
142 | on_release: root.show_floating_buttons()
143 | ''')
144 |
145 |
146 | class MDFloatingLabel(MDCard):
147 | text = StringProperty()
148 | text_color = ListProperty([0, 0, 0, 1])
149 |
150 | class MDStackFloatingButtons(FloatLayout):
151 | icon = StringProperty('checkbox-blank-circle')
152 | callback = ObjectProperty(lambda x: None)
153 | text_color = ListProperty([0, 0, 0, 1])
154 | floating_data = DictProperty()
155 | show = False
156 | in_progress = False
157 |
158 | def __init__(self, **kwargs):
159 | super().__init__(**kwargs)
160 |
161 | self.lbl_list = [self.ids.f_lbl_1, self.ids.f_lbl_2, self.ids.f_lbl_3]
162 | self.btn_list = [self.ids.f_btn_1, self.ids.f_btn_2, self.ids.f_btn_3]
163 |
164 | def set_in_progress(self, instance_anim, instance):
165 | if instance is self.ids.f_btn_3:
166 | self.in_progress = False
167 |
168 | def show_floating_buttons(self):
169 | step = dp(46)
170 | if self.in_progress:
171 | return
172 | self.in_progress = True
173 | for i, btn in enumerate(self.btn_list):
174 | step += dp(56)
175 | anim = Animation(y=step, d=.5, t='out_elastic')
176 | anim.bind(on_complete=self.set_in_progress)
177 | anim.start(btn)
178 |
179 | self.show = True if not self.show else False
180 | self.show_floating_labels() if self.show \
181 | else self.hide_floating_labels()
182 |
183 | def show_floating_labels(self):
184 | i = 0
185 | for lbl in self.lbl_list:
186 | i += .3
187 | pos_x = Window.width - (lbl.width + dp(46 + 21 * 1.5))
188 | Animation(x=pos_x, d=i, t='out_elastic').start(lbl)
189 |
190 | def hide_floating_buttons(self):
191 | for btn in self.btn_list:
192 | Animation(y=25, d=.5, t='in_elastic').start(btn)
193 |
194 | def hide_floating_labels(self):
195 | i = 1
196 | for lbl in self.lbl_list:
197 | i -= .3
198 | Animation(x=-lbl.width, d=i, t='out_elastic').start(lbl)
199 | self.hide_floating_buttons()
200 |
--------------------------------------------------------------------------------
/kivymd/stiffscroll/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 LogicalDash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/kivymd/stiffscroll/README.md:
--------------------------------------------------------------------------------
1 | stiffscroll
2 | ===========
3 |
4 | A ScrollEffect for use with a Kivy ScrollView. It makes scrolling more
5 | laborious as you reach the edge of the scrollable area.
6 |
7 | A ScrollView constructed with StiffScrollEffect,
8 | eg. ScrollView(effect_cls=StiffScrollEffect), will get harder to
9 | scroll as you get nearer to its edges. You can scroll all the way to
10 | the edge if you want to, but it will take more finger-movement than
11 | usual.
12 |
13 | Unlike DampedScrollEffect, it is impossible to overscroll with
14 | StiffScrollEffect. That means you cannot push the contents of the
15 | ScrollView far enough to see what's beneath them. This is appropriate
16 | if the ScrollView contains, eg., a background image, like a desktop
17 | wallpaper. Overscrolling may give the impression that there is some
18 | reason to overscroll, even if just to take a peek beneath, and that
19 | impression may be misleading.
20 |
21 | StiffScrollEffect was written by Zachary Spector. His other stuff is at:
22 | https://github.com/LogicalDash/
23 | He can be reached, and possibly hired, at:
24 | zacharyspector@gmail.com
--------------------------------------------------------------------------------
/kivymd/stiffscroll/__init__.py:
--------------------------------------------------------------------------------
1 | """An Effect to be used with ScrollView to prevent scrolling beyond
2 | the bounds, but politely.
3 |
4 | A ScrollView constructed with StiffScrollEffect,
5 | eg. ScrollView(effect_cls=StiffScrollEffect), will get harder to
6 | scroll as you get nearer to its edges. You can scroll all the way to
7 | the edge if you want to, but it will take more finger-movement than
8 | usual.
9 |
10 | Unlike DampedScrollEffect, it is impossible to overscroll with
11 | StiffScrollEffect. That means you cannot push the contents of the
12 | ScrollView far enough to see what's beneath them. This is appropriate
13 | if the ScrollView contains, eg., a background image, like a desktop
14 | wallpaper. Overscrolling may give the impression that there is some
15 | reason to overscroll, even if just to take a peek beneath, and that
16 | impression may be misleading.
17 |
18 | StiffScrollEffect was written by Zachary Spector. His other stuff is at:
19 | https://github.com/LogicalDash/
20 | He can be reached, and possibly hired, at:
21 | zacharyspector@gmail.com
22 |
23 | """
24 |
25 | from time import time
26 |
27 | from kivy.animation import AnimationTransition
28 | from kivy.effects.kinetic import KineticEffect
29 | from kivy.properties import ObjectProperty, NumericProperty
30 | from kivy.uix.widget import Widget
31 |
32 |
33 | class StiffScrollEffect(KineticEffect):
34 | drag_threshold = NumericProperty('20sp')
35 | """Minimum distance to travel before the movement is considered as a
36 | drag."""
37 |
38 | min = NumericProperty(0)
39 | """Minimum boundary to stop the scrolling at."""
40 |
41 | max = NumericProperty(0)
42 | """Maximum boundary to stop the scrolling at."""
43 |
44 | max_friction = NumericProperty(1)
45 | """How hard should it be to scroll, at the worst?"""
46 |
47 | body = NumericProperty(0.7)
48 | """Proportion of the range in which you can scroll unimpeded."""
49 |
50 | scroll = NumericProperty(0.)
51 | """Computed value for scrolling"""
52 |
53 | transition_min = ObjectProperty(AnimationTransition.in_cubic)
54 | """The AnimationTransition function to use when adjusting the friction
55 | near the minimum end of the effect.
56 |
57 | """
58 |
59 | transition_max = ObjectProperty(AnimationTransition.in_cubic)
60 | """The AnimationTransition function to use when adjusting the friction
61 | near the maximum end of the effect.
62 |
63 | """
64 |
65 | target_widget = ObjectProperty(None, allownone=True, baseclass=Widget)
66 | """The widget to apply the effect to."""
67 |
68 | displacement = NumericProperty(0)
69 | """The absolute distance moved in either direction."""
70 |
71 | scroll = NumericProperty(0.)
72 | """The distance to be used for scrolling."""
73 |
74 | def __init__(self, **kwargs):
75 | """Set ``self.base_friction`` to the value of ``self.friction`` just
76 | after instantiation, so that I can reset to that value later.
77 |
78 | """
79 |
80 | super().__init__(**kwargs)
81 | self.base_friction = self.friction
82 |
83 | def update_velocity(self, dt):
84 | """Before actually updating my velocity, meddle with ``self.friction``
85 | to make it appropriate to where I'm at, currently.
86 |
87 | """
88 |
89 | hard_min = self.min
90 | hard_max = self.max
91 | if hard_min > hard_max:
92 | hard_min, hard_max = hard_max, hard_min
93 |
94 | margin = (1. - self.body) * (hard_max - hard_min)
95 | soft_min = hard_min + margin
96 | soft_max = hard_max - margin
97 |
98 | if self.value < soft_min:
99 | try:
100 | prop = (soft_min - self.value) / (soft_min - hard_min)
101 | self.friction = self.base_friction + abs(
102 | self.max_friction - self.base_friction
103 | ) * self.transition_min(prop)
104 | except ZeroDivisionError:
105 | pass
106 | elif self.value > soft_max:
107 | try:
108 | # normalize how far past soft_max I've gone as a
109 | # proportion of the distance between soft_max and hard_max
110 | prop = (self.value - soft_max) / (hard_max - soft_max)
111 | self.friction = self.base_friction + abs(
112 | self.max_friction - self.base_friction
113 | ) * self.transition_min(prop)
114 | except ZeroDivisionError:
115 | pass
116 | else:
117 | self.friction = self.base_friction
118 |
119 | return super().update_velocity(dt)
120 |
121 | def on_value(self, *args):
122 | """Prevent moving beyond my bounds, and update ``self.scroll``"""
123 |
124 | if self.value < self.min:
125 | self.velocity = 0
126 | self.scroll = self.min
127 | elif self.value > self.max:
128 | self.velocity = 0
129 | self.scroll = self.max
130 | else:
131 | self.scroll = self.value
132 |
133 | def start(self, val, t=None):
134 | """Start movement with ``self.friction`` = ``self.base_friction``"""
135 |
136 | self.is_manual = True
137 | t = t or time()
138 | self.velocity = self.displacement = 0
139 | self.friction = self.base_friction
140 | self.history = [(t, val)]
141 |
142 | def update(self, val, t=None):
143 | """Reduce the impact of whatever change has been made to me, in
144 | proportion with my current friction.
145 |
146 | """
147 |
148 | t = t or time()
149 | hard_min = self.min
150 | hard_max = self.max
151 | if hard_min > hard_max:
152 | hard_min, hard_max = hard_max, hard_min
153 |
154 | gamut = hard_max - hard_min
155 | margin = (1. - self.body) * gamut
156 | soft_min = hard_min + margin
157 | soft_max = hard_max - margin
158 | distance = val - self.history[-1][1]
159 | reach = distance + self.value
160 |
161 | if (
162 | distance < 0 and reach < soft_min) or (
163 | distance > 0 and soft_max < reach):
164 | distance -= distance * self.friction
165 | self.apply_distance(distance)
166 | self.history.append((t, val))
167 |
168 | if len(self.history) > self.max_history:
169 | self.history.pop(0)
170 | self.displacement += abs(distance)
171 | self.trigger_velocity_update()
172 |
173 | def stop(self, val, t=None):
174 | """Work out whether I've been flung."""
175 |
176 | self.is_manual = False
177 | self.displacement += abs(val - self.history[-1][1])
178 | if self.displacement <= self.drag_threshold:
179 | self.velocity = 0
180 |
181 | return super().stop(val, t)
182 |
--------------------------------------------------------------------------------
/kivymd/theming_dynamic_text.py:
--------------------------------------------------------------------------------
1 | """
2 | Bottom Sheets
3 | =============
4 |
5 | Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors -
6 | KivyMD library up to version 0.1.2
7 | Copyright (c) 2019 Ivanov Yuri and KivyMD contributors -
8 | KivyMD library version 0.1.3 and higher
9 |
10 | For suggestions and questions:
11 |
12 |
13 | This file is distributed under the terms of the same license,
14 | as the Kivy framework.
15 |
16 | Two implementations. The first is based on color brightness obtained from:-
17 | https://www.w3.org/TR/AERT#color-contrast
18 | The second is based on relative luminance calculation for sRGB obtained from:-
19 | https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
20 | and contrast ratio calculation obtained from:-
21 | https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
22 |
23 | Preliminary testing suggests color brightness more closely matches the
24 | Material Design spec suggested text colors, but the alternative implementation
25 | is both newer and the current 'correct' recommendation, so is included here
26 | as an option.
27 | """
28 |
29 |
30 | def _color_brightness(color):
31 | # Implementation of color brightness method
32 | brightness = color[0] * 299 + color[1] * 587 + color[2] * 114
33 | brightness = brightness
34 | return brightness
35 |
36 |
37 | def _black_or_white_by_color_brightness(color):
38 | if _color_brightness(color) >= 500:
39 | return 'black'
40 | else:
41 | return 'white'
42 |
43 |
44 | def _normalized_channel(color):
45 | # Implementation of contrast ratio and relative luminance method
46 | if color <= .03928:
47 | return color / 12.92
48 | else:
49 | return ((color + .055) / 1.055) ** 2.4
50 |
51 |
52 | def _luminance(color):
53 | rg = _normalized_channel(color[0])
54 | gg = _normalized_channel(color[1])
55 | bg = _normalized_channel(color[2])
56 | return .2126*rg + .7152*gg + .0722*bg
57 |
58 |
59 | def _black_or_white_by_contrast_ratio(color):
60 | l_color = _luminance(color)
61 | l_black = .0
62 | l_white = 1.0
63 | b_contrast = (l_color + .05) / (l_black + .05)
64 | w_contrast = (l_white + .05) / (l_color + .05)
65 | return 'white' if w_contrast >= b_contrast else 'black'
66 |
67 |
68 | def get_contrast_text_color(color, use_color_brightness=True):
69 | if use_color_brightness:
70 | contrast_color = _black_or_white_by_color_brightness(color)
71 | else:
72 | contrast_color = _black_or_white_by_contrast_ratio(color)
73 | if contrast_color == 'white':
74 | return 1, 1, 1, 1
75 | else:
76 | return 0, 0, 0, 1
77 |
78 |
79 | if __name__ == '__main__':
80 | from kivy.utils import get_color_from_hex
81 | from kivymd.color_definitions import colors, text_colors
82 |
83 | for c in colors.items():
84 | if c[0] in ['Light', 'Dark']:
85 | continue
86 | color = c[0]
87 | print(f"For the {color} color palette:")
88 | for name, hex_color in c[1].items():
89 | if hex_color:
90 | col = get_color_from_hex(hex_color)
91 | col_bri = get_contrast_text_color(col)
92 | con_rat = get_contrast_text_color(
93 | col, use_color_brightness=False)
94 | text_color = text_colors[c[0]][name]
95 | print(
96 | f" The {name} hue gives {col_bri} using color "
97 | f"brightness, {con_rat} using contrast ratio, and "
98 | f"{text_color} from the MD spec")
99 |
--------------------------------------------------------------------------------
/kivymd/time_picker.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from kivy.lang import Builder
4 | from kivy.uix.modalview import ModalView
5 | from kivy.uix.floatlayout import FloatLayout
6 | from kivymd.theming import ThemableBehavior
7 | from kivymd.elevationbehavior import RectangularElevationBehavior
8 | from kivy.properties import ObjectProperty, ListProperty
9 |
10 | Builder.load_string("""
11 | #:import MDFlatButton kivymd.button.MDFlatButton
12 | #:import CircularTimePicker kivymd.vendor.circularTimePicker.CircularTimePicker
13 | #:import dp kivy.metrics.dp
14 | :
15 | size_hint: (None, None)
16 | size: [dp(270), dp(335)+dp(95)]
17 | #if root.theme_cls.device_orientation == 'portrait' else [dp(520), dp(325)]
18 | pos_hint: {'center_x': .5, 'center_y': .5}
19 | canvas:
20 | Color:
21 | rgba: self.theme_cls.bg_light
22 | Rectangle:
23 | size: [dp(270), dp(335)]
24 | #if root.theme_cls.device_orientation == 'portrait' else [dp(250), root.height]
25 | pos: [root.pos[0], root.pos[1] + root.height - dp(335) - dp(95)]
26 | #if root.theme_cls.device_orientation == 'portrait' else [root.pos[0]+dp(270), root.pos[1]]
27 | Color:
28 | rgba: self.theme_cls.primary_color
29 | Rectangle:
30 | size: [dp(270), dp(95)]
31 | #if root.theme_cls.device_orientation == 'portrait' else [dp(270), root.height]
32 | pos: [root.pos[0], root.pos[1] + root.height - dp(95)]
33 | #if root.theme_cls.device_orientation == 'portrait' else [root.pos[0], root.pos[1]]
34 | Color:
35 | rgba: self.theme_cls.bg_dark
36 | Ellipse:
37 | size: [dp(220), dp(220)]
38 | #if root.theme_cls.device_orientation == 'portrait' else [dp(195), dp(195)]
39 | pos: root.pos[0]+dp(270)/2-dp(220)/2, root.pos[1] + root.height - (dp(335)/2+dp(95)) - dp(220)/2 + dp(35)
40 | #Color:
41 | #rgba: (1, 0, 0, 1)
42 | #Line:
43 | #width: 4
44 | #points: dp(270)/2, root.height, dp(270)/2, 0
45 | CircularTimePicker:
46 | id: time_picker
47 | pos: (dp(270)/2)-(self.width/2), root.height-self.height
48 | size_hint: [.8, .8]
49 | #if root.theme_cls.device_orientation == 'portrait' else [0.35, 0.9]
50 | pos_hint: {'center_x': 0.5, 'center_y': 0.585}
51 | #if root.theme_cls.device_orientation == 'portrait' else {'center_x': 0.75, 'center_y': 0.7}
52 | MDFlatButton:
53 | width: dp(32)
54 | id: ok_button
55 | pos: root.pos[0]+root.size[0]-self.width-dp(10), root.pos[1] + dp(10)
56 | text: "OK"
57 | on_release: root.close_ok()
58 | MDFlatButton:
59 | id: cancel_button
60 | pos: root.pos[0]+root.size[0]-self.width-ok_button.width-dp(10), root.pos[1] + dp(10)
61 | text: "Cancel"
62 | on_release: root.close_cancel()
63 | """)
64 |
65 |
66 | class MDTimePicker(ThemableBehavior, FloatLayout, ModalView,
67 | RectangularElevationBehavior):
68 | # md_bg_color = ListProperty((0, 0, 0, 0))
69 | time = ObjectProperty()
70 |
71 | def __init__(self, **kwargs):
72 | super(MDTimePicker, self).__init__(**kwargs)
73 | self.current_time = self.ids.time_picker.time
74 |
75 | def set_time(self, time):
76 | try:
77 | self.ids.time_picker.set_time(time)
78 | except AttributeError:
79 | raise TypeError("MDTimePicker._set_time must receive a datetime object, not a \"" +
80 | type(time).__name__ + "\"")
81 |
82 | def close_cancel(self):
83 | self.dismiss()
84 |
85 | def close_ok(self):
86 | self.current_time = self.ids.time_picker.time
87 | self.time = self.current_time
88 | self.dismiss()
89 |
--------------------------------------------------------------------------------
/kivymd/toast/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Brian - androidtoast library
4 | Copyright (c) 2019 Ivanov Yuri - kivytoast library
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | this software and associated documentation files (the "Software"), to deal in
8 | the Software without restriction, including without limitation the rights to
9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software is furnished to do so,
11 | subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/kivymd/toast/README.md:
--------------------------------------------------------------------------------
1 | KivyToast
2 | ========
3 |
4 | A package for working with messages like Toast on Android. It is intended for use in applications written using the Kivy framework.
5 |
6 | This package is an improved version of the package https://github.com/knappador/kivy-toaster in which human toasts are written, written on Kivy.
7 |
8 |
9 |
10 | The package modules are written using the framework for cross-platform development of .
11 | Information about the framework is available at http://kivy.org.
12 |
13 | An example of usage (note that with this import the native implementation of toasts will be used for the Android platform and implementation on Kivy for others:
14 |
15 | ```python
16 | from toast import toast
17 |
18 | ...
19 |
20 | # And then in the code, toasts are available
21 | # by calling the toast function:
22 | toast ('Your message')
23 | ```
24 |
25 | To force the Kivy implementation on the Android platform, use the import of the form:
26 |
27 | ```python
28 | from toast.kivytoast import toast
29 | ```
30 |
31 | PROGRAMMING LANGUAGE
32 | ----------------------
33 | Python 2.7 +
34 |
35 | DEPENDENCE
36 | -----------
37 | The [Kivy] framework (http://kivy.org/docs/installation/installation.html)
38 |
39 | LICENSE
40 | --------
41 | MIT
--------------------------------------------------------------------------------
/kivymd/toast/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Toast
3 | =====
4 |
5 | Copyright (c) 2013 Brian Knapp - androidtoast module
6 | Copyright (c) 2019 Ivanov Yuri - kivytoast module
7 |
8 | For suggestions and questions:
9 |
10 |
11 | This file is distributed under the terms of the same license,
12 | as the Kivy framework.
13 | """
14 |
15 | from kivy import platform
16 |
17 |
18 | if platform == 'android':
19 | from . androidtoast import toast
20 | else:
21 | from . kivytoast import toast
22 |
--------------------------------------------------------------------------------
/kivymd/toast/androidtoast/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | AndroidToast
3 | ============
4 |
5 | Copyright (c) 2013 Brian Knapp
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 | """
13 |
14 | from . androidtoast import toast
15 |
--------------------------------------------------------------------------------
/kivymd/toast/androidtoast/androidtoast.py:
--------------------------------------------------------------------------------
1 | """
2 | AndroidToast
3 | ============
4 |
5 | Copyright (c) 2013 Brian Knapp
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 | """
13 |
14 | from kivy.logger import Logger
15 | from jnius import autoclass, PythonJavaClass, java_method, cast
16 | from android import activity
17 | from android.runnable import run_on_ui_thread
18 |
19 | Toast = autoclass('android.widget.Toast')
20 | context = autoclass('org.kivy.android.PythonActivity').mActivity
21 |
22 | @run_on_ui_thread
23 | def toast(text, length_long=False):
24 | duration = Toast.LENGTH_LONG if length_long else Toast.LENGTH_SHORT
25 | String = autoclass('java.lang.String')
26 | c = cast('java.lang.CharSequence', String(text))
27 | t = Toast.makeText(context, c, duration)
28 | t.show()
29 |
--------------------------------------------------------------------------------
/kivymd/toast/kivytoast/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | KivyToast
3 | =========
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 | """
13 |
14 | from . kivytoast import toast
15 |
--------------------------------------------------------------------------------
/kivymd/toast/kivytoast/kivytoast.py:
--------------------------------------------------------------------------------
1 | """
2 | KivyToast
3 | =========
4 |
5 | Copyright (c) 2019 Ivanov Yuri
6 |
7 | For suggestions and questions:
8 |
9 |
10 | This file is distributed under the terms of the same license,
11 | as the Kivy framework.
12 | """
13 |
14 | from kivy.core.window import Window
15 | from kivy.uix.label import Label
16 | from kivy.animation import Animation
17 | from kivy.uix.modalview import ModalView
18 | from kivy.clock import Clock
19 | from kivy.metrics import dp
20 | from kivy.lang import Builder
21 |
22 | from kivymd import images_path
23 |
24 | Builder.load_string("""
25 | :
26 | canvas:
27 | Color:
28 | rgba: .2, .2, .2, 1
29 | RoundedRectangle:
30 | pos: self.pos
31 | size: self.size
32 | radius: [15,]
33 | """)
34 |
35 |
36 | class Toast(ModalView):
37 | def __init__(self, **kwargs):
38 | super().__init__(**kwargs)
39 | self.size_hint = (None, None)
40 | self.pos_hint = {'center_x': .5, 'center_y': .1}
41 | self.background_color = [0, 0, 0, 0]
42 | self.background = f'{images_path}transparent.png'
43 | self.opacity = 0
44 | self.auto_dismiss = True
45 | self.label_toast = Label(size_hint=(None, None), opacity=0)
46 | self.label_toast.bind(texture_size=self.label_check_texture_size)
47 | self.add_widget(self.label_toast)
48 |
49 | def label_check_texture_size(self, instance, texture_size):
50 | texture_width, texture_height = texture_size
51 | if texture_width > Window.width:
52 | instance.text_size = (Window.width - dp(10), None)
53 | instance.texture_update()
54 | texture_width, texture_height = instance.texture_size
55 | self.size = (texture_width + 25, texture_height + 25)
56 |
57 | def toast(self, text_toast):
58 | self.label_toast.text = text_toast
59 | self.open()
60 |
61 | def on_open(self):
62 | self.fade_in()
63 | Clock.schedule_once(self.fade_out, 2.5)
64 |
65 | def fade_in(self):
66 | Animation(opacity=1, duration=.4).start(self.label_toast)
67 | Animation(opacity=1, duration=.4).start(self)
68 |
69 | def fade_out(self, interval):
70 | Animation(opacity=0, duration=.4).start(self.label_toast)
71 | anim_body = Animation(opacity=0, duration=.4)
72 | anim_body.bind(on_complete=lambda *x: self.dismiss())
73 | anim_body.start(self)
74 |
75 | def on_touch_down(self, touch):
76 | if not self.collide_point(*touch.pos):
77 | if self.auto_dismiss:
78 | self.dismiss()
79 | return False
80 | super(ModalView, self).on_touch_down(touch)
81 | return True
82 |
83 |
84 | def toast(text, length_long=False):
85 | Toast().toast(text)
86 |
--------------------------------------------------------------------------------
/kivymd/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019 Ivanov Yuri
3 |
4 | For suggestions and questions:
5 |
6 |
7 | This file is distributed under the terms of the same license,
8 | as the Kivy framework.
9 |
10 | """
11 |
--------------------------------------------------------------------------------
/kivymd/utils/asynckivy.py:
--------------------------------------------------------------------------------
1 | """
2 | asynckivy
3 | =========
4 |
5 | Copyright (c) 2019 Nattōsai Mitō
6 |
7 | GitHub -
8 | https://github.com/gottadiveintopython
9 | GitHub Gist -
10 | https://gist.github.com/gottadiveintopython/5f4a775849f9277081c396de65dc57c1
11 |
12 | """
13 |
14 | __all__ = ('start', 'sleep', 'event', )
15 |
16 | import types
17 | from functools import partial
18 | from collections import namedtuple
19 | from kivy.clock import Clock
20 |
21 | CallbackParameter = namedtuple('CallbackParameter', ('args', 'kwargs', ))
22 |
23 |
24 | def start(coro):
25 | def step(*args, **kwargs):
26 | try:
27 | coro.send(CallbackParameter(args, kwargs))(step)
28 | except StopIteration:
29 | pass
30 |
31 | try:
32 | coro.send(None)(step)
33 | except StopIteration:
34 | pass
35 |
36 |
37 | @types.coroutine
38 | def sleep(duration):
39 | # The partial() here looks meaningless. But this is needed in order
40 | # to avoid weak reference.
41 | param = yield lambda step_coro: Clock.schedule_once(
42 | partial(step_coro), duration)
43 | return param.args[0]
44 |
45 |
46 | class event:
47 | def __init__(self, ed, name):
48 | self.bind_id = None
49 | self.ed = ed
50 | self.name = name
51 |
52 | def bind(self, step_coro):
53 | self.bind_id = bind_id = self.ed.fbind(self.name, self.callback)
54 | assert bind_id > 0 # check if binding succeeded
55 | self.step_coro = step_coro
56 |
57 | def callback(self, *args, **kwargs):
58 | self.parameter = CallbackParameter(args, kwargs)
59 | ed = self.ed
60 | ed.unbind_uid(self.name, self.bind_id)
61 | self.step_coro()
62 |
63 | def __await__(self):
64 | yield self.bind
65 | return self.parameter
66 |
--------------------------------------------------------------------------------
/kivymd/utils/cropimage.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019 Ivanov Yuri
3 |
4 | For suggestions and questions:
5 |
6 |
7 | This file is distributed under the terms of the same license,
8 | as the Kivy framework.
9 |
10 | """
11 |
12 |
13 | def crop_image(cutting_size, path_to_image, path_to_save_crop_image,
14 | corner=0, blur=0, corner_mode='all'):
15 | """Call functions of cropping/blurring/rounding image.
16 |
17 | cutting_size: size to which the image will be cropped;
18 | path_to_image: path to origin image;
19 | path_to_save_crop_image: path to new image;
20 | corner: value of rounding corners;
21 | blur: blur value;
22 | corner_mode: 'all'/'top'/'bottom' - indicates which corners to round out;
23 |
24 | """
25 |
26 | im = _crop_image(cutting_size, path_to_image, path_to_save_crop_image)
27 | if corner:
28 | im = add_corners(im, corner, corner_mode)
29 | if blur:
30 | im = add_blur(im, blur)
31 | try:
32 | im.save(path_to_save_crop_image)
33 | except IOError:
34 | im.save(path_to_save_crop_image, 'JPEG')
35 |
36 |
37 | def add_blur(im, mode):
38 | from PIL import ImageFilter
39 |
40 | im = im.filter(ImageFilter.GaussianBlur(mode))
41 |
42 | return im
43 |
44 |
45 | def _crop_image(cutting_size, path_to_image, path_to_save_crop_image):
46 | from PIL import Image, ImageOps
47 |
48 | image = Image.open(path_to_image)
49 | image = ImageOps.fit(image, cutting_size)
50 | image.save(path_to_save_crop_image)
51 |
52 | return image
53 |
54 |
55 | def add_corners(im, corner, corner_mode):
56 | def add_top_corners():
57 | alpha.paste(circle.crop((0, 0, corner, corner)), (0, 0))
58 | alpha.paste(circle.crop(
59 | (corner, 0, corner * 2, corner)), (w - corner, 0))
60 | print(corner)
61 |
62 | def add_bottom_corners():
63 | alpha.paste(circle.crop(
64 | (0, corner, corner, corner * 2)), (0, h - corner))
65 | alpha.paste(
66 | circle.crop((corner, corner, corner * 2, corner * 2)), (w - corner, h - corner))
67 | print(corner)
68 |
69 | from PIL import Image, ImageDraw
70 |
71 | circle = Image.new('L', (corner * 2, corner * 2), 0)
72 | draw = ImageDraw.Draw(circle)
73 | draw.ellipse((0, 0, corner * 2, corner * 2), fill=255)
74 | alpha = Image.new('L', im.size, 255)
75 | w, h = im.size
76 |
77 | if corner_mode == 'all':
78 | add_top_corners()
79 | add_bottom_corners()
80 | elif corner_mode == 'top':
81 | add_top_corners()
82 | if corner_mode == 'bottom':
83 | add_bottom_corners()
84 | im.putalpha(alpha)
85 |
86 | return im
87 |
88 |
89 | def prepare_mask(size, antialias=2):
90 | from PIL import Image, ImageDraw
91 |
92 | mask = Image.new('L', (size[0] * antialias, size[1] * antialias), 0)
93 | ImageDraw.Draw(mask).ellipse((0, 0) + mask.size, fill=255)
94 | return mask.resize(size, Image.ANTIALIAS)
95 |
96 |
97 | def _crop_round_image(im, s):
98 | from PIL import Image
99 |
100 | w, h = im.size
101 | k = w // s[0] - h // s[1]
102 | if k > 0:
103 | im = im.crop(((w - h) // 2, 0, (w + h) // 2, h))
104 | elif k < 0:
105 | im = im.crop((0, (h - w) // 2, w, (h + w) // 2))
106 | return im.resize(s, Image.ANTIALIAS)
107 |
108 |
109 | def crop_round_image(cutting_size, path_to_image, path_to_new_image):
110 | from PIL import Image
111 |
112 | im = Image.open(path_to_image)
113 | im = _crop_round_image(im, cutting_size)
114 | im.putalpha(prepare_mask(cutting_size, 4))
115 | im.save(path_to_new_image)
--------------------------------------------------------------------------------
/kivymd/vendor/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcroni/KivyLiteEmulator/380c81f5cb3f2a7a18049a186a3c4b853869e7ab/kivymd/vendor/__init__.py
--------------------------------------------------------------------------------
/kivymd/vendor/circleLayout/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Davide Depau
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 |
23 |
--------------------------------------------------------------------------------
/kivymd/vendor/circleLayout/README.md:
--------------------------------------------------------------------------------
1 | CircularLayout
2 | ==============
3 |
4 | CircularLayout is a special layout that places widgets around a circle.
5 |
6 | See the widget's documentation and the example for more information.
7 |
8 | 
9 |
10 | size_hint
11 | ---------
12 |
13 | size_hint_x is used as an angle-quota hint (widget with higher
14 | size_hint_x will be farther from each other, and viceversa), while
15 | size_hint_y is used as a widget size hint (widgets with a higher size
16 | hint will be bigger).size_hint_x cannot be None.
17 |
18 | Widgets are all squares, unless you set size_hint_y to None (in that
19 | case you'll be able to specify your own size), and their size is the
20 | difference between the outer and the inner circle's radii. To make the
21 | widgets bigger you can just decrease inner_radius_hint.
--------------------------------------------------------------------------------
/kivymd/vendor/circleLayout/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | CircularLayout
3 | ==============
4 |
5 | CircularLayout is a special layout that places widgets around a circle.
6 |
7 | size_hint
8 | ---------
9 |
10 | size_hint_x is used as an angle-quota hint (widget with higher
11 | size_hint_x will be farther from each other, and vice versa), while
12 | size_hint_y is used as a widget size hint (widgets with a higher size
13 | hint will be bigger).size_hint_x cannot be None.
14 |
15 | Widgets are all squares, unless you set size_hint_y to None (in that
16 | case you'll be able to specify your own size), and their size is the
17 | difference between the outer and the inner circle's radii. To make the
18 | widgets bigger you can just decrease inner_radius_hint.
19 | """
20 |
21 | from math import sin, cos, pi, radians
22 |
23 | from kivy.uix.layout import Layout
24 | from kivy.properties import NumericProperty, ReferenceListProperty, \
25 | OptionProperty, BoundedNumericProperty, VariableListProperty, AliasProperty
26 |
27 | __all__ = ('CircularLayout')
28 |
29 | try:
30 | xrange(1, 2)
31 | except NameError:
32 | def xrange(first, second, third=None):
33 | if third:
34 | return range(first, second, third)
35 | else:
36 | return range(first, second)
37 |
38 |
39 | class CircularLayout(Layout):
40 | """
41 | Circular layout class. See module documentation for more information.
42 | """
43 |
44 | padding = VariableListProperty([0, 0, 0, 0])
45 | """Padding between the layout box and it's children: [padding_left,
46 | padding_top, padding_right, padding_bottom].
47 |
48 | padding also accepts a two argument form [padding_horizontal,
49 | padding_vertical] and a one argument form [padding].
50 |
51 | .. version changed:: 1.7.0
52 | Replaced NumericProperty with VariableListProperty.
53 |
54 | :attr:`padding` is a :class:`~kivy.properties.VariableListProperty` and
55 | defaults to [0, 0, 0, 0].
56 | """
57 |
58 | start_angle = NumericProperty(0)
59 | """Angle (in degrees) at which the first widget will be placed.
60 | Start counting angles from the X axis, going counterclockwise.
61 |
62 | :attr:`start_angle` is a :class:`~kivy.properties.NumericProperty` and
63 | defaults to 0 (start from the right).
64 | """
65 |
66 | circle_quota = BoundedNumericProperty(360, min=0, max=360)
67 | """Size (in degrees) of the part of the circumference that will actually
68 | be used to place widgets.
69 |
70 | :attr:`circle_quota` is a :class:`~kivy.properties.BoundedNumericProperty`
71 | and defaults to 360 (all the circumference).
72 | """
73 |
74 | direction = OptionProperty("ccw", options=("cw", "ccw"))
75 | """Direction of widgets in the circle.
76 |
77 | :attr:`direction` is an :class:`~kivy.properties.OptionProperty` and
78 | defaults to 'ccw'. Can be 'ccw' (counterclockwise) or 'cw' (clockwise).
79 | """
80 |
81 | outer_radius_hint = NumericProperty(1)
82 | """Sets the size of the outer circle. A number greater than 1 will make the
83 | widgets larger than the actual widget, a number smaller than 1 will leave
84 | a gap.
85 |
86 | :attr:`outer_radius_hint` is a :class:`~kivy.properties.NumericProperty`
87 | and defaults to 1.
88 | """
89 |
90 | inner_radius_hint = NumericProperty(.6)
91 | """Sets the size of the inner circle. A number greater than
92 | :attr:`outer_radius_hint` will cause glitches. The closest it is to
93 | :attr:`outer_radius_hint`, the smallest will be the widget in the layout.
94 |
95 | :attr:`outer_radius_hint` is a :class:`~kivy.properties.NumericProperty`
96 | and defaults to 1.
97 | """
98 |
99 | radius_hint = ReferenceListProperty(inner_radius_hint, outer_radius_hint)
100 | """Combined :attr:`outer_radius_hint` and :attr:`inner_radius_hint`
101 | in a list for convenience. See their documentation for more details.
102 |
103 | :attr:`radius_hint` is a :class:`~kivy.properties.ReferenceListProperty`.
104 | """
105 |
106 | def _get_delta_radii(self):
107 | radius = min(self.width - self.padding[0] - self.padding[2],
108 | self.height - self.padding[1] - self.padding[3]) / 2.
109 | outer_r = radius * self.outer_radius_hint
110 | inner_r = radius * self.inner_radius_hint
111 | return outer_r - inner_r
112 | delta_radii = AliasProperty(_get_delta_radii, None,
113 | bind=("radius_hint", "padding", "size"))
114 |
115 | def __init__(self, **kwargs):
116 | super().__init__(**kwargs)
117 |
118 | self.bind(
119 | start_angle=self._trigger_layout,
120 | parent=self._trigger_layout,
121 | # padding=self._trigger_layout,
122 | children=self._trigger_layout,
123 | size=self._trigger_layout,
124 | radius_hint=self._trigger_layout,
125 | pos=self._trigger_layout)
126 |
127 | def do_layout(self, *largs):
128 | # optimize layout by preventing looking at the same attribute in a loop
129 | len_children = len(self.children)
130 | if len_children == 0:
131 | return
132 | selfcx = self.center_x
133 | selfcy = self.center_y
134 | direction = self.direction
135 | cquota = radians(self.circle_quota)
136 | start_angle_r = radians(self.start_angle)
137 | padding_left = self.padding[0]
138 | padding_top = self.padding[1]
139 | padding_right = self.padding[2]
140 | padding_bottom = self.padding[3]
141 | padding_x = padding_left + padding_right
142 | padding_y = padding_top + padding_bottom
143 |
144 | radius = min(self.width-padding_x, self.height-padding_y) / 2.
145 | outer_r = radius * self.outer_radius_hint
146 | inner_r = radius * self.inner_radius_hint
147 | middle_r = radius * sum(self.radius_hint) / 2.
148 | delta_r = outer_r - inner_r
149 |
150 | stretch_weight_angle = 0.
151 | for w in self.children:
152 | sha = w.size_hint_x
153 | if sha is None:
154 | raise ValueError(
155 | "size_hint_x cannot be None in a CircularLayout")
156 | else:
157 | stretch_weight_angle += sha
158 |
159 | sign = +1.
160 | angle_offset = start_angle_r
161 | if direction == 'cw':
162 | angle_offset = 2 * pi - start_angle_r
163 | sign = -1.
164 |
165 | for c in reversed(self.children):
166 | sha = c.size_hint_x
167 | shs = c.size_hint_y
168 |
169 | angle_quota = cquota / stretch_weight_angle * sha
170 | angle = angle_offset + (sign * angle_quota / 2)
171 | angle_offset += sign * angle_quota
172 |
173 | # kived: looking it up, yes. x = cos(angle) * radius + centerx;
174 | # y = sin(angle) * radius + centery
175 | ccx = cos(angle) * middle_r + selfcx + padding_left - padding_right
176 | ccy = sin(angle) * middle_r + selfcy + padding_bottom - padding_top
177 |
178 | c.center_x = ccx
179 | c.center_y = ccy
180 | if shs:
181 | s = delta_r * shs
182 | c.width = s
183 | c.height = s
184 |
185 |
186 | if __name__ == "__main__":
187 | from kivy.app import App
188 | from kivy.uix.button import Button
189 |
190 | class CircLayoutApp(App):
191 | def build(self):
192 | cly = CircularLayout(direction="cw", start_angle=-75,
193 | inner_radius_hint=.7, padding="20dp")
194 |
195 | for i in xrange(1, 13):
196 | cly.add_widget(Button(text=str(i), font_size="30dp"))
197 |
198 | return cly
199 |
200 | CircLayoutApp().run()
201 |
--------------------------------------------------------------------------------
/kivymd/vendor/circularTimePicker/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Davide Depau
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 |
23 |
--------------------------------------------------------------------------------
/kivymd/vendor/circularTimePicker/README.md:
--------------------------------------------------------------------------------
1 | Circular Date & Time Picker for Kivy
2 | ====================================
3 |
4 | (currently only time, date coming soon)
5 |
6 | Based on [CircularLayout](https://github.com/kivy-garden/garden.circularlayout).
7 | The main aim is to provide a date and time selector similar to the
8 | one found in Android KitKat+.
9 |
10 | 
11 |
12 | Simple usage
13 | ------------
14 |
15 | Import the widget with
16 |
17 | ```python
18 | from kivy.garden.circulardatetimepicker import CircularTimePicker
19 | ```
20 |
21 | then use it! That's it!
22 |
23 | ```python
24 | c = CircularTimePicker()
25 | c.bind(time=self.set_time)
26 | root.add_widget(c)
27 | ```
28 |
29 | in Kv language:
30 |
31 | ```
32 | :
33 | BoxLayout:
34 | orientation: "vertical"
35 |
36 | CircularTimePicker
37 |
38 | Button:
39 | text: "Dismiss"
40 | size_hint_y: None
41 | height: "40dp"
42 | on_release: root.dismiss()
43 | ```
--------------------------------------------------------------------------------
/kivymd/vendor/navigationdrawer/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Alexander Taylor
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/kivymd/vendor/navigationdrawer/README.md:
--------------------------------------------------------------------------------
1 | # NavigationDrawer
2 |
3 | The NavigationDrawer widget provides a hidden panel view designed to
4 | duplicate the popular Android layout. The user views one main widget
5 | but can slide from the left of the screen to view a second, previously
6 | hidden widget. The transition between open/closed is smoothly
7 | animated, with the parameters (anim time, panel width, touch
8 | detection) all user configurable. If the panel is released without
9 | being fully open or closed, it animates to an appropriate
10 | configuration.
11 |
12 | NavigationDrawer supports many different animation properties,
13 | including moving one or both of the side/main panels, darkening
14 | either/both widgets, changing side panel opacity, and changing which
15 | widget is on top. The user can edit these individually to taste (this
16 | is enough rope to hang oneself, it's easy to make a useless or silly
17 | configuration!), or use one of a few preset animations.
18 |
19 | The hidden panel might normally a set of navigation buttons (e.g. in a
20 | GridLayout), but the implementation lets the user use any widget(s).
21 |
22 | The first widget added to the NavigationDrawer is automatically used
23 | as the side panel, and the second widget as the main panel. No further
24 | widgets can be added, further changes are left to the user via editing
25 | the panel widgets.
26 |
27 | # Usage summary
28 |
29 | - The first widget added to a NavigationDrawer is used as the hidden
30 | side panel.
31 | - The second widget added is used as the main panel.
32 | - Both widgets can be removed with remove_widget, or alternatively
33 | set/removed with set_main_panel and set_side_panel.
34 | - The hidden side panel can be revealed by dragging from the left of
35 | the NavigationDrawer. The touch detection width is the
36 | touch_accept_width property.
37 | - Every animation property is user-editable, or default animations
38 | can be chosen by setting anim_type.
39 |
40 | See the example and docstrings for information on individual properties.
41 |
42 |
43 | # Example::
44 |
45 | from kivy.app import App
46 | from kivy.base import runTouchApp
47 | from kivy.uix.boxlayout import BoxLayout
48 | from kivy.uix.label import Label
49 | from kivy.uix.button import Button
50 | from kivy.uix.image import Image
51 | from kivy.uix.widget import Widget
52 | from kivy.core.window import Window
53 | from kivy.metrics import dp
54 |
55 | from kivy.garden.navigationdrawer import NavigationDrawer
56 |
57 | class ExampleApp(App):
58 |
59 | def build(self):
60 | navigationdrawer = NavigationDrawer()
61 |
62 | side_panel = BoxLayout(orientation='vertical')
63 | side_panel.add_widget(Label(text='Panel label'))
64 | side_panel.add_widget(Button(text='A button'))
65 | side_panel.add_widget(Button(text='Another button'))
66 | navigationdrawer.add_widget(side_panel)
67 |
68 | label_head = (
69 | '[b]Example label filling main panel[/b]\n\n[color=ff0000](p'
70 | 'ull from left to right!)[/color]\n\nIn this example, the le'
71 | 'ft panel is a simple boxlayout menu, and this main panel is'
72 | ' a BoxLayout with a label and example image.\n\nSeveral pre'
73 | 'set layouts are available (see buttons below), but users ma'
74 | 'y edit every parameter for much more customisation.')
75 | main_panel = BoxLayout(orientation='vertical')
76 | label_bl = BoxLayout(orientation='horizontal')
77 | label = Label(text=label_head, font_size='15sp',
78 | markup=True, valign='top')
79 | label_bl.add_widget(Widget(size_hint_x=None, width=dp(10)))
80 | label_bl.add_widget(label)
81 | label_bl.add_widget(Widget(size_hint_x=None, width=dp(10)))
82 | main_panel.add_widget(Widget(size_hint_y=None, height=dp(10)))
83 | main_panel.add_widget(label_bl)
84 | main_panel.add_widget(Widget(size_hint_y=None, height=dp(10)))
85 | main_panel.add_widget(Image(source='red_pixel.png', allow_stretch=True,
86 | keep_ratio=False, size_hint_y=0.2))
87 | navigationdrawer.add_widget(main_panel)
88 | label.bind(size=label.setter('text_size'))
89 |
90 | def set_anim_type(name):
91 | navigationdrawer.anim_type = name
92 | modes_layout = BoxLayout(orientation='horizontal')
93 | modes_layout.add_widget(Label(text='preset\nanims:'))
94 | slide_an = Button(text='slide_\nabove_\nanim')
95 | slide_an.bind(on_press=lambda j: set_anim_type('slide_above_anim'))
96 | slide_sim = Button(text='slide_\nabove_\nsimple')
97 | slide_sim.bind(on_press=lambda j: set_anim_type('slide_above_simple'))
98 | fade_in_button = Button(text='fade_in')
99 | fade_in_button.bind(on_press=lambda j: set_anim_type('fade_in'))
100 | reveal_button = Button(text='reveal_\nbelow_\nanim')
101 | reveal_button.bind(on_press=
102 | lambda j: set_anim_type('reveal_below_anim'))
103 | slide_button = Button(text='reveal_\nbelow_\nsimple')
104 | slide_button.bind(on_press=
105 | lambda j: set_anim_type('reveal_below_simple'))
106 | modes_layout.add_widget(slide_an)
107 | modes_layout.add_widget(slide_sim)
108 | modes_layout.add_widget(fade_in_button)
109 | modes_layout.add_widget(reveal_button)
110 | modes_layout.add_widget(slide_button)
111 | main_panel.add_widget(modes_layout)
112 |
113 | button = Button(text='toggle NavigationDrawer state (animate)',
114 | size_hint_y=0.2)
115 | button.bind(on_press=lambda j: navigationdrawer.toggle_state())
116 | button2 = Button(text='toggle NavigationDrawer state (jump)',
117 | size_hint_y=0.2)
118 | button2.bind(on_press=lambda j: navigationdrawer.toggle_state(False))
119 | button3 = Button(text='toggle _main_above', size_hint_y=0.2)
120 | button3.bind(on_press=navigationdrawer.toggle_main_above)
121 | main_panel.add_widget(button)
122 | main_panel.add_widget(button2)
123 | main_panel.add_widget(button3)
124 |
125 | return navigationdrawer
126 |
127 | ExampleApp().run()
128 |
129 |
--------------------------------------------------------------------------------
/test.kv:
--------------------------------------------------------------------------------
1 | :
2 | MDIconButton:
3 | icon: 'emoticon-excited'
4 | id:get_a_joke
5 | pos_hint: {'center_x': 0.4, 'center_y': 0.75}
6 | size_hint: None, None
7 | size: dp(45), dp(45)
8 |
9 | MDFloatingActionButton:
10 | id:chevron_up
11 | icon: 'share-variant'
12 | opacity: 0.9
13 | pos_hint: {'center_x': 0.8, 'center_y': 0.2}
14 | size_hint: None, None
15 | size: dp(45), dp(45)
16 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | from kivy.app import App
2 | from kivy.uix.boxlayout import BoxLayout
3 | from kivy.uix.button import Button
4 | from kivy.uix.floatlayout import FloatLayout
5 |
6 | from kivymd.theming import ThemeManager
7 |
8 |
9 | class Interface(FloatLayout):
10 | pass
11 |
12 |
13 | class Test(App):
14 | theme_cls = ThemeManager()
15 | theme_cls.primary_palette = 'Indigo'
16 | theme_cls.accent_palette = 'Indigo'
17 | def build(self):
18 | return Interface()
19 |
20 |
21 | if __name__ == '__main__':
22 | Test().run()
--------------------------------------------------------------------------------