Added Navigation Drawer UI with create feature

This commit is contained in:
surbhi 2018-07-18 18:19:39 +05:30
parent 3b4d73d291
commit bb8d4c5a1f
No known key found for this signature in database
GPG Key ID: 88928762974D3618
73 changed files with 8734 additions and 225 deletions

43
android_instruction.rst Normal file
View File

@ -0,0 +1,43 @@
PyBitmessage(Android)
This sample aims to be as close to a real world example of a mobile. It has a more refined design and also provides a practical example of how a mobile app would interact and communicate with its adresses.
Steps for trying out this sample:
Compile and install the mobile app onto your mobile device or emulator.
Getting Started
This sample uses the kivy as Kivy is an open source, cross-platform Python framework for the development of applications that make use of innovative, multi-touch user interfaces. The aim is to allow for quick and easy interaction design and rapid prototyping whilst making your code reusable and deployable.
Kivy is written in Python and Cython, supports various input devices and has an extensive widget library. With the same codebase, you can target Windows, OS X, Linux, Android and iOS. All Kivy widgets are built with multitouch support.
Kivy in support take Buildozer which is a tool that automates the entire build process. It downloads and sets up all the prequisites for python-for-android, including the android SDK and NDK, then builds an apk that can be automatically pushed to the device.
Buildozer currently works only in Linux, and is an alpha release, but it already works well and can significantly simplify the apk build.
To build this project, use the "Buildozer android release deploy run" command or use.
Buildozer ue=sed for creating application packages easily.The goal is to have one "buildozer.spec" file in your app directory, describing your application requirements and settings such as title, icon, included modules etc. Buildozer will use that spec to create a package for Android, iOS, Windows, OSX and/or Linux.
Installing Requirements
You can create a package for android using the python-for-android project as with using the Buildozer tool to automate the entire process. You can also see Packaging your application for the Kivy Launcher to run kivy programs without compiling them.
You can get buildozer at https://github.com/kivy/buildozer or you can directly install using pip install buildozer
This will install buildozer in your system. Afterwards, navigate to your project directory and run:
buildozer init
This creates a buildozer.spec file controlling your build configuration. You should edit it appropriately with your app name etc. You can set variables to control most or all of the parameters passed to python-for-android.
Install buildozers dependencies.
Finally, plug in your android device and run:
buildozer android debug deploy run >> To build, push and automatically run the apk on your device. Here we used debug as tested in debug mode for now.
Packaging your application for the Kivy Launcher

View File

@ -1,167 +1,173 @@
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<Display>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1, None
<Navigator>:
id: nav_drawer
NavigationDrawerIconButton:
Button:
id: btn
background_color:app.theme_cls.primary_dark
text: 'BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk'
on_release: dropdown.open(self)
pos_hint: {'x': 0, 'y': 0.3}
size_hint_y: None
height: '48dp'
DropDown:
id: dropdown
on_parent: self.dismiss()
on_select: btn.text = '{}'.format(args[1])
Button:
text: 'messages'
on_release: sm.current = 'screen_one'
background_color: (0,0,0,0)
color: 0,0,0,1
Button:
text: 'send'
on_release: sm.current = 'screen_two'
background_color: (0,0,0,0)
color: 0,0,0,1
Button:
text: 'subscription'
on_release: sm.current = 'screen_three'
background_color: (0,0,0,0)
color: 0,0,0,1
Button:
text: 'four'
on_release: sm.current = 'screen_four'
background_color: (0,0,0,0)
color: 0,0,0,1
background_color:app.theme_cls.primary_dark
text: 'BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk')
on_release: app.root.ids.scr_mngr.current = 'dialog'
Button:
text: 'chans'
on_release: sm.current = 'screen_five'
background_color: (0,0,0,0)
color: 0,0,0,1
Button:
text: 'blacklist'
on_release: sm.current = 'screen_six'
background_color: (0,0,0,0)
color: 0,0,0,1
background_color:app.theme_cls.primary_dark
text: 'BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('BM-2cSvewtJw8o8eqcFzmLeXHza4BVsRsirKk')
on_release: app.root.ids.scr_mngr.current = 'button'
ScreenManager:
id: sm
transition: FadeTransition()
Screen_One:
Screen_Two:
Screen_three:
Screen_four:
Screen_five:
Screen_six:
EmailScreen:
<Screen_One>:
name: 'screen_one'
BoxLayout:
padding: 10,10,10,300
orientation: "vertical"
NavigationDrawerIconButton:
icon: 'email'
text: "inbox"
on_release: app.root.ids.scr_mngr.current = 'inbox'
NavigationDrawerIconButton:
icon: 'email'
text: "sent"
on_release: app.root.ids.scr_mngr.current = 'sent'
NavigationDrawerIconButton:
icon: 'email'
text: "trash"
on_release: app.root.ids.scr_mngr.current = 'trash'
NavigationDrawerIconButton:
icon: 'email'
text: "dialog"
on_release: app.root.ids.scr_mngr.current = 'dialog'
NavigationDrawerIconButton:
icon: 'email'
text: "test"
on_release: app.root.ids.scr_mngr.current = 'test'
BoxLayout:
orientation: 'vertical'
Toolbar:
id: toolbar
title: 'PyBitmessage'
background_color: app.theme_cls.primary_dark
left_action_items: [['menu', lambda x: app.nav_drawer.toggle()]]
Button:
text: "create"
size_hint_y: 0.3
size_hint_x: 0.1
text:"EXIT"
color: 0,0,0,1
background_color: (0,0,0,0)
Button:
text: "inbox"
background_color: (0,0,0,0)
size_hint_y: 0.3
size_hint_y: 0.1
size_hint_x: 0.1
color: 0,0,0,1
Button:
text: "new"
background_color: (0,0,0,0)
size_hint_y: 0.3
size_hint_x: 0.1
color: 0,0,0,1
on_press:
root.manager.current = 'email'
Button:
text: "sents"
background_color: (0,0,0,0)
size_hint_y: 0.3
size_hint_x: 0.1
color: 0,0,0,1
pos_hint: {'x': 0.8, 'y':0.4}
on_press: app.say_exit()
<Screen_Two>:
name: 'screen_two'
ScreenManager:
id: scr_mngr
Inbox:
id:sc1
Sent:
id:sc2
Trash:
id:sc3
Dialog:
id:sc4
Test:
id:sc5
Create:
id:sc6
Button:
height:100
size_hint_y: 0.1
size_hint_x: 0.1
pos_hint: {'x': 0.85, 'y': 0.5}
background_color: (0,0,0,0)
on_press: scr_mngr.current = 'create'
Image:
source: 'images/plus.png'
y: self.parent.y
x: self.parent.x + self.parent.width - 50
size: 70, 70
<Inbox>:
name: 'inbox'
<Sent>:
name: 'sent'
<Trash>:
name: 'trash'
Label:
text: "Two"
<Screen_three>:
name: 'screen_three'
text:"I am in card"
color: 0,0,0,1
<Dialog>:
name: 'dialog'
Label:
text: "Three"
<Screen_four>:
name: 'screen_four'
text:"I have a good dialox box"
color: 0,0,0,1
<Test>:
name: 'test'
Label:
text: "four"
text:"a rapid data env"
color: 0,0,0,1
<Screen_five>:
name: 'screen_five'
Label:
text: "Two"
<Screen_six>:
name: 'screen_six'
Label:
text: "Two"
<EmailScreen>:
name: 'email'
BoxLayout:
padding: 100,10,100,100
id: login_layout
orientation: "vertical"
GridLayout:
rows: 2
orientation: 'vertical'
spacing: 10
<Create>:
name: 'create'
GridLayout:
rows: 5
cols: 1
padding: 100,100,100,100
spacing: 50
BoxLayout:
size_hint_y: None
height: '32dp'
Label:
text: 'From: '
background_color: (0,0,0,0)
text: 'FROM'
color: 0,0,0,1
font_size: 18
pos_hint: {"x":.15,"y":1}
text_size: root.width-20, 20
TextInput:
multiline:False
size_hint: 1,.4
hint_text: 'email'
GridLayout:
rows: 2
orientation: 'vertical'
BoxLayout:
size_hint_y: None
height: '32dp'
Label:
text: 'To: '
background_color: (0,0,0,0)
text: 'TO'
color: 0,0,0,1
font_size: 18
pos_hint: {"x":.15,"y":.5}
text_size: root.width-20, 20
TextInput:
multiline:False
size_hint: 1,.4
pos_hint: {"x":.2,"y":.5}
hint_text: 'email'
GridLayout:
rows: 2
orientation: 'vertical'
BoxLayout:
size_hint_y: None
height: '32dp'
Label:
text: 'Message'
background_color: (0,0,0,0)
text: 'SUBJECT'
color: 0,0,0,1
pos_hint: {"x":.15,"y":.5}
font_size: 18
text_size: root.width-20, 20
TextInput:
hint_text: 'subject'
BoxLayout:
size_hint_y: None
height: '32dp'
Label:
text: 'BODY'
color: 0,0,0,1
TextInput:
id: msg
multiline:True
size_hint: 1,2
Button:
text: "SEND"
size_hint_y: 0.1
size_hint_x: 0.2
pos_hint: {"x":.15,"y":1}
Button:
text: 'SEND'
size_hint_y: 0.1
size_hint_x: 0.3
height: '32dp'
pos_hint: {'x': .5, 'y': 0.1}

View File

@ -1,97 +1,79 @@
import time
import os
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty, StringProperty
from kivymd.theming import ThemeManager
from kivy.uix.widget import Widget
from navigationdrawer import NavigationDrawer
from kivymd.toolbar import Toolbar
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.uix.boxlayout import BoxLayout
# from kivy.uix.gridlayout import GridLayout
from debug import logger
from addresses import addBMIfNotPresent, decodeAddress
from bmconfigparser import BMConfigParser
from helper_ackPayload import genAckPayload
from helper_sql import sqlExecute
import queues
from kivy.core.window import Window
from os import environ
import shutdown
statusIconColor = 'red'
class NavigateApp(App):
theme_cls = ThemeManager()
nav_drawer = ObjectProperty()
class LoginScreen(BoxLayout):
"""This will use for sending message to recipents from mobile client."""
def build(self):
main_widget = Builder.load_file(os.path.join(os.path.dirname(__file__), 'main.kv'))
self.nav_drawer = Navigator()
return main_widget
def send(self):
"""It is used for sending message with subject and body."""
queues.apiAddressGeneratorReturnQueue.queue.clear()
streamNumberForAddress = 1
label = "CisDevelper"
eighteenByteRipe = False
nonceTrialsPerByte = 1000
payloadLengthExtraBytes = 1000
queues.addressGeneratorQueue.put((
'createRandomAddress', 4,
streamNumberForAddress, label, 1,
"", eighteenByteRipe, nonceTrialsPerByte,
payloadLengthExtraBytes
))
fromAddress = queues.apiAddressGeneratorReturnQueue.get()
toAddress = "BM-2cWyUfBdY2FbgyuCb7abFZ49JYxSzUhNFe"
message = self.ids.user_input.text
subject = 'Test'
encoding = 3
sendMessageToPeople = True
if sendMessageToPeople:
if toAddress != '':
status, addressVersionNumber, streamNumber, ripe = decodeAddress(
toAddress)
if status == 'success':
toAddress = addBMIfNotPresent(toAddress)
if addressVersionNumber > 4 or addressVersionNumber <= 1:
logger.info("addressVersionNumber > 4 or addressVersionNumber <= 1")
if streamNumber > 1 or streamNumber == 0:
logger.info("streamNumber > 1 or streamNumber == 0")
if statusIconColor == 'red':
logger.info("shared.statusIconColor == 'red'")
stealthLevel = BMConfigParser().safeGetInt(
'bitmessagesettings', 'ackstealthlevel')
ackdata = genAckPayload(streamNumber, stealthLevel)
sqlExecute(
'''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''',
'',
toAddress,
ripe,
fromAddress,
subject,
message,
ackdata,
int(time.time()),
int(time.time()),
0,
'msgqueued',
0,
'sent',
encoding,
BMConfigParser().getint('bitmessagesettings', 'ttl'))
queues.workerQueue.put(('sendmessage', toAddress))
return None
def sayexit(self):
"""Method will exit the application screen."""
def say_exit(self):
print ("**************************EXITING FROM APPLICATION*****************************")
App.get_running_app().stop()
shutdown.doCleanShutdown()
Window.close()
class Navigator(NavigationDrawer):
image_source = StringProperty('images/me.jpg')
title = StringProperty('Navigation')
class MainApp(App):
"""The App class is the base for creating Kivy applications
Think of it as your main entry point into the Kivy run loop."""
def build(self):
"""To initialize an app with a widget tree, we need to override the build().
method in our app class and return the widget tree which we have constructed..
"""
return LoginScreen()
class Inbox(Screen):
def __init__ (self,**kwargs):
super (Inbox, self).__init__(**kwargs)
val_y = .1
val_z = 0
my_box1 = BoxLayout(orientation='vertical')
for i in range(1, 5):
my_box1.add_widget(Label(text="I am in inbox", size_hint = (.3,.1), pos_hint = {'x': val_z,'top': val_y},color = (0,0,0,1), background_color = (0,0,0,0)))
val_y+=.1
self.add_widget(my_box1)
class Sent(Screen):
def __init__ (self,**kwargs):
super (Sent, self).__init__(**kwargs)
val_y = .1
val_z = 0
my_box1 = BoxLayout(orientation='vertical')
for i in range(1, 5):
my_box1.add_widget(Label(text="I am in sent", size_hint = (.3,.1), pos_hint = {'x': val_z,'top': val_y},color = (0,0,0,1), background_color = (0,0,0,0)))
val_y+=.1
self.add_widget(my_box1)
class Trash(Screen):
pass
class Dialog(Screen):
pass
class Test(Screen):
pass
class Create(Screen):
pass
if __name__ == '__main__':
NavigateApp().run()

View File

@ -398,8 +398,8 @@ class Main:
bitmessagecurses.runwrapper()
elif state.kivy:
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
from bitmessagekivy.mpybit import MainApp
MainApp().run()
from bitmessagekivy.mpybit import NavigateApp
NavigateApp().run()
else:
import bitmessageqt
bitmessageqt.run()

BIN
src/images/kivymd_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
src/images/me.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
src/images/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

21
src/kivymd/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrés Rodríguez and KivyMD contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

6
src/kivymd/__init__.py Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
import os
path = os.path.dirname(__file__)
fonts_path = os.path.join(path, "fonts/")
images_path = os.path.join(path, 'images/')

254
src/kivymd/accordion.py Normal file
View File

@ -0,0 +1,254 @@
# -*- coding: utf-8 -*-
from kivy.lang import Builder
from kivy.properties import StringProperty, ListProperty, OptionProperty
from kivy.utils import get_color_from_hex
from kivymd.color_definitions import colors
from kivymd.theming import ThemableBehavior
from kivy.uix.accordion import Accordion, AccordionItem
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
from kivy.uix.boxlayout import BoxLayout
class MDAccordionItemTitleLayout(ThemableBehavior, BackgroundColorBehavior, BoxLayout):
pass
class MDAccordion(ThemableBehavior, BackgroundColorBehavior, Accordion):
pass
class MDAccordionItem(ThemableBehavior, AccordionItem):
title_theme_color = OptionProperty(None, allownone=True,
options=['Primary', 'Secondary', 'Hint',
'Error', 'Custom'])
''' Color theme for title text and icon '''
title_color = ListProperty(None, allownone=True)
''' Color for title text and icon if `title_theme_color` is Custom '''
background_color = ListProperty(None, allownone=True)
''' Color for the background of the accordian item title in rgba format.
'''
divider_color = ListProperty(None, allownone=True)
''' Color for dividers between different titles in rgba format
To remove the divider set a color with an alpha of 0.
'''
indicator_color = ListProperty(None, allownone=True)
''' Color for the indicator on the side of the active item in rgba format
To remove the indicator set a color with an alpha of 0.
'''
font_style = OptionProperty(
'Subhead', options=['Body1', 'Body2', 'Caption', 'Subhead', 'Title',
'Headline', 'Display1', 'Display2', 'Display3',
'Display4', 'Button', 'Icon'])
''' Font style to use for the title text '''
title_template = StringProperty('MDAccordionItemTitle')
''' Template to use for the title '''
icon = StringProperty(None,allownone=True)
''' Icon name to use when this item is expanded '''
icon_expanded = StringProperty('chevron-up')
''' Icon name to use when this item is expanded '''
icon_collapsed = StringProperty('chevron-down')
''' Icon name to use when this item is collapsed '''
Builder.load_string('''
#:import MDLabel kivymd.label.MDLabel
#:import md_icons kivymd.icon_definitions.md_icons
<MDAccordionItem>:
canvas.before:
Color:
rgba: self.background_color or self.theme_cls.primary_color
Rectangle:
size:self.size
pos:self.pos
PushMatrix
Translate:
xy: (dp(2),0) if self.orientation == 'vertical' else (0,dp(2))
canvas.after:
PopMatrix
Color:
rgba: self.divider_color or self.theme_cls.divider_color
Rectangle:
size:(dp(1),self.height) if self.orientation == 'horizontal' else (self.width,dp(1))
pos:self.pos
Color:
rgba: [0,0,0,0] if self.collapse else (self.indicator_color or self.theme_cls.accent_color)
Rectangle:
size:(dp(2),self.height) if self.orientation == 'vertical' else (self.width,dp(2))
pos:self.pos
[MDAccordionItemTitle@MDAccordionItemTitleLayout]:
padding: '12dp'
spacing: '12dp'
orientation: 'horizontal' if ctx.item.orientation=='vertical' else 'vertical'
canvas:
PushMatrix
Translate:
xy: (-dp(2),0) if ctx.item.orientation == 'vertical' else (0,-dp(2))
Color:
rgba: self.background_color or self.theme_cls.primary_color
Rectangle:
size:self.size
pos:self.pos
canvas.after:
Color:
rgba: [0,0,0,0] if ctx.item.collapse else (ctx.item.indicator_color or self.theme_cls.accent_color)
Rectangle:
size:(dp(2),self.height) if ctx.item.orientation == 'vertical' else (self.width,dp(2))
pos:self.pos
PopMatrix
MDLabel:
id:_icon
theme_text_color:ctx.item.title_theme_color if ctx.item.icon else 'Custom'
text_color:ctx.item.title_color if ctx.item.icon else [0,0,0,0]
text: md_icons[ctx.item.icon if ctx.item.icon else 'menu']
font_style:'Icon'
size_hint: (None,1) if ctx.item.orientation == 'vertical' else (1,None)
size: ((self.texture_size[0],1) if ctx.item.orientation == 'vertical' else (1,self.texture_size[1])) \
if ctx.item.icon else (0,0)
text_size: (self.width, None) if ctx.item.orientation=='vertical' else (None,self.width)
canvas.before:
PushMatrix
Rotate:
angle: 90 if ctx.item.orientation == 'horizontal' else 0
origin: self.center
canvas.after:
PopMatrix
MDLabel:
id:_label
theme_text_color:ctx.item.title_theme_color
text_color:ctx.item.title_color
text: ctx.item.title
font_style:ctx.item.font_style
text_size: (self.width, None) if ctx.item.orientation=='vertical' else (None,self.width)
canvas.before:
PushMatrix
Rotate:
angle: 90 if ctx.item.orientation == 'horizontal' else 0
origin: self.center
canvas.after:
PopMatrix
MDLabel:
id:_expand_icon
theme_text_color:ctx.item.title_theme_color
text_color:ctx.item.title_color
font_style:'Icon'
size_hint: (None,1) if ctx.item.orientation == 'vertical' else (1,None)
size: (self.texture_size[0],1) if ctx.item.orientation == 'vertical' else (1,self.texture_size[1])
text:md_icons[ctx.item.icon_collapsed if ctx.item.collapse else ctx.item.icon_expanded]
halign: 'right' if ctx.item.orientation=='vertical' else 'center'
#valign: 'middle' if ctx.item.orientation=='vertical' else 'bottom'
canvas.before:
PushMatrix
Rotate:
angle: 90 if ctx.item.orientation == 'horizontal' else 0
origin:self.center
canvas.after:
PopMatrix
''')
if __name__ == '__main__':
from kivy.app import App
from kivymd.theming import ThemeManager
class AccordionApp(App):
theme_cls = ThemeManager()
def build(self):
# self.theme_cls.primary_palette = 'Indigo'
return Builder.load_string("""
#:import MDLabel kivymd.label.MDLabel
#:import MDList kivymd.list.MDList
#:import OneLineListItem kivymd.list.OneLineListItem
BoxLayout:
spacing: '64dp'
MDAccordion:
orientation:'vertical'
MDAccordionItem:
title:'Item 1'
icon: 'home'
ScrollView:
MDList:
OneLineListItem:
text: "Subitem 1"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
OneLineListItem:
text: "Subitem 2"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
OneLineListItem:
text: "Subitem 3"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
MDAccordionItem:
title:'Item 2'
icon: 'globe'
ScrollView:
MDList:
OneLineListItem:
text: "Subitem 4"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
OneLineListItem:
text: "Subitem 5"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
OneLineListItem:
text: "Subitem 6"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
MDAccordionItem:
title:'Item 3'
ScrollView:
MDList:
OneLineListItem:
text: "Subitem 7"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
OneLineListItem:
text: "Subitem 8"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
OneLineListItem:
text: "Subitem 9"
theme_text_color: 'Custom'
text_color: [1,1,1,1]
MDAccordion:
orientation:'horizontal'
MDAccordionItem:
title:'Item 1'
icon: 'home'
MDLabel:
text:'Content 1'
theme_text_color:'Primary'
MDAccordionItem:
title:'Item 2'
MDLabel:
text:'Content 2'
theme_text_color:'Primary'
MDAccordionItem:
title:'Item 3'
MDLabel:
text:'Content 3'
theme_text_color:'Primary'
""")
AccordionApp().run()

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from kivy.lang import Builder
from kivy.properties import BoundedNumericProperty, ReferenceListProperty
from kivy.uix.widget import Widget
Builder.load_string('''
<BackgroundColorBehavior>
canvas:
Color:
rgba: self.background_color
Rectangle:
size: self.size
pos: self.pos
''')
class BackgroundColorBehavior(Widget):
r = BoundedNumericProperty(1., min=0., max=1.)
g = BoundedNumericProperty(1., min=0., max=1.)
b = BoundedNumericProperty(1., min=0., max=1.)
a = BoundedNumericProperty(0., min=0., max=1.)
background_color = ReferenceListProperty(r, g, b, a)

211
src/kivymd/bottomsheet.py Normal file
View File

@ -0,0 +1,211 @@
# -*- coding: utf-8 -*-
'''
Bottom Sheets
=============
`Material Design spec Bottom Sheets page <http://www.google.com/design/spec/components/bottom-sheets.html>`_
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.
Examples
--------
.. note::
These widgets are designed to be called from Python code only.
For :class:`MDListBottomSheet`:
.. code-block:: python
bs = MDListBottomSheet()
bs.add_item("Here's an item with text only", lambda x: x)
bs.add_item("Here's an item with an icon", lambda x: x, icon='md-cast')
bs.add_item("Here's another!", lambda x: x, icon='md-nfc')
bs.open()
For :class:`MDListBottomSheet`:
.. code-block:: python
bs = MDGridBottomSheet()
bs.add_item("Facebook", lambda x: x, icon_src='./assets/facebook-box.png')
bs.add_item("YouTube", lambda x: x, icon_src='./assets/youtube-play.png')
bs.add_item("Twitter", lambda x: x, icon_src='./assets/twitter.png')
bs.add_item("Da Cloud", lambda x: x, icon_src='./assets/cloud-upload.png')
bs.add_item("Camera", lambda x: x, icon_src='./assets/camera.png')
bs.open()
API
---
'''
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.modalview import ModalView
from kivy.uix.scrollview import ScrollView
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
from kivymd.label import MDLabel
from kivymd.list import MDList, OneLineListItem, ILeftBody, \
OneLineIconListItem
from kivymd.theming import ThemableBehavior
Builder.load_string('''
<MDBottomSheet>
background: 'atlas://data/images/defaulttheme/action_group_disabled'
background_color: 0,0,0,.8
sv: sv
upper_padding: upper_padding
gl_content: gl_content
ScrollView:
id: sv
do_scroll_x: False
BoxLayout:
size_hint_y: None
orientation: 'vertical'
padding: 0,1,0,0
height: upper_padding.height + gl_content.height + 1 # +1 to allow overscroll
BsPadding:
id: upper_padding
size_hint_y: None
height: root.height - min(root.width * 9 / 16, gl_content.height)
on_release: root.dismiss()
BottomSheetContent:
id: gl_content
size_hint_y: None
background_color: root.theme_cls.bg_normal
cols: 1
''')
class BsPadding(ButtonBehavior, FloatLayout):
pass
class BottomSheetContent(BackgroundColorBehavior, GridLayout):
pass
class MDBottomSheet(ThemableBehavior, ModalView):
sv = ObjectProperty()
upper_padding = ObjectProperty()
gl_content = ObjectProperty()
dismiss_zone_scroll = 1000 # Arbitrary high number
def open(self, *largs):
super(MDBottomSheet, self).open(*largs)
Clock.schedule_once(self.set_dismiss_zone, 0)
def set_dismiss_zone(self, *largs):
# Scroll to right below overscroll threshold:
self.sv.scroll_y = 1 - self.sv.convert_distance_to_scroll(0, 1)[1]
# This is a line where m (slope) is 1/6 and b (y-intercept) is 80:
self.dismiss_zone_scroll = self.sv.convert_distance_to_scroll(
0, (self.height - self.upper_padding.height) * (1 / 6.0) + 80)[
1]
# Uncomment next line if the limit should just be half of
# visible content on open (capped by specs to 16 units to width/9:
# self.dismiss_zone_scroll = (self.sv.convert_distance_to_scroll(
# 0, self.height - self.upper_padding.height)[1] * 0.50)
# Check if user has overscrolled enough to dismiss bottom sheet:
self.sv.bind(on_scroll_stop=self.check_if_scrolled_to_death)
def check_if_scrolled_to_death(self, *largs):
if self.sv.scroll_y >= 1 + self.dismiss_zone_scroll:
self.dismiss()
def add_widget(self, widget, index=0):
if type(widget) == ScrollView:
super(MDBottomSheet, self).add_widget(widget, index)
else:
self.gl_content.add_widget(widget,index)
Builder.load_string('''
#:import md_icons kivymd.icon_definitions.md_icons
<ListBSIconLeft>
font_style: 'Icon'
text: u"{}".format(md_icons[root.icon])
halign: 'center'
theme_text_color: 'Primary'
valign: 'middle'
''')
class ListBSIconLeft(ILeftBody, MDLabel):
icon = StringProperty()
class MDListBottomSheet(MDBottomSheet):
mlist = ObjectProperty()
def __init__(self, **kwargs):
super(MDListBottomSheet, self).__init__(**kwargs)
self.mlist = MDList()
self.gl_content.add_widget(self.mlist)
Clock.schedule_once(self.resize_content_layout, 0)
def resize_content_layout(self, *largs):
self.gl_content.height = self.mlist.height
def add_item(self, text, callback, icon=None):
if icon:
item = OneLineIconListItem(text=text, on_release=callback)
item.add_widget(ListBSIconLeft(icon=icon))
else:
item = OneLineListItem(text=text, on_release=callback)
item.bind(on_release=lambda x: self.dismiss())
self.mlist.add_widget(item)
Builder.load_string('''
<GridBSItem>
orientation: 'vertical'
padding: 0, dp(24), 0, 0
size_hint_y: None
size: dp(64), dp(96)
BoxLayout:
padding: dp(8), 0, dp(8), dp(8)
size_hint_y: None
height: dp(48)
Image:
source: root.source
MDLabel:
font_style: 'Caption'
theme_text_color: 'Secondary'
text: root.caption
halign: 'center'
''')
class GridBSItem(ButtonBehavior, BoxLayout):
source = StringProperty()
caption = StringProperty()
class MDGridBottomSheet(MDBottomSheet):
def __init__(self, **kwargs):
super(MDGridBottomSheet, self).__init__(**kwargs)
self.gl_content.padding = (dp(16), 0, dp(16), dp(24))
self.gl_content.height = dp(24)
self.gl_content.cols = 3
def add_item(self, text, callback, icon_src):
item = GridBSItem(
caption=text,
on_release=callback,
source=icon_src
)
item.bind(on_release=lambda x: self.dismiss())
if len(self.gl_content.children) % 3 == 0:
self.gl_content.height += dp(96)
self.gl_content.add_widget(item)

453
src/kivymd/button.py Normal file
View File

@ -0,0 +1,453 @@
# -*- coding: utf-8 -*-
'''
Buttons
=======
`Material Design spec, Buttons page <https://www.google.com/design/spec/components/buttons.html>`_
`Material Design spec, Buttons: Floating Action Button page <https://www.google.com/design/spec/components/buttons-floating-action-button.html>`_
TO-DO: DOCUMENT MODULE
'''
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.metrics import dp
from kivy.utils import get_color_from_hex
from kivy.properties import StringProperty, BoundedNumericProperty, \
ListProperty, AliasProperty, BooleanProperty, NumericProperty, \
OptionProperty
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.animation import Animation
from kivymd.backgroundcolorbehavior import BackgroundColorBehavior
from kivymd.ripplebehavior import CircularRippleBehavior, \
RectangularRippleBehavior
from kivymd.elevationbehavior import ElevationBehavior, \
RoundElevationBehavior
from kivymd.theming import ThemableBehavior
from kivymd.color_definitions import colors
Builder.load_string('''
#:import md_icons kivymd.icon_definitions.md_icons
#:import colors kivymd.color_definitions.colors
#:import MDLabel kivymd.label.MDLabel
<MDIconButton>
size_hint: (None, None)
size: (dp(48), dp(48))
padding: dp(12)
theme_text_color: 'Primary'
MDLabel:
id: _label
font_style: 'Icon'
text: u"{}".format(md_icons[root.icon])
halign: 'center'
theme_text_color: root.theme_text_color
text_color: root.text_color
opposite_colors: root.opposite_colors
valign: 'middle'
<MDFlatButton>
canvas:
Color:
#rgba: self.background_color if self.state == 'normal' else self._bg_color_down
rgba: self._current_button_color
Rectangle:
size: self.size
pos: self.pos
size_hint: (None, None)
height: dp(36)
width: _label.texture_size[0] + dp(16)
padding: (dp(8), 0)
theme_text_color: 'Custom'
text_color: root.theme_cls.primary_color
MDLabel:
id: _label
text: root._text
font_style: 'Button'
size_hint_x: None
text_size: (None, root.height)
height: self.texture_size[1]
theme_text_color: root.theme_text_color
text_color: root.text_color
valign: 'middle'
halign: 'center'
opposite_colors: root.opposite_colors
<MDRaisedButton>:
canvas:
Clear
Color:
rgba: self.background_color_disabled if self.disabled else \
(self.background_color if self.state == 'normal' else self.background_color_down)
Rectangle:
size: self.size
pos: self.pos
anchor_x: 'center'
anchor_y: 'center'
background_color: root.theme_cls.primary_color
background_color_down: root.theme_cls.primary_dark
background_color_disabled: root.theme_cls.divider_color
theme_text_color: 'Primary'
MDLabel:
id: label
font_style: 'Button'
text: root._text
size_hint: None, None
width: root.width
text_size: self.width, None
height: self.texture_size[1]
theme_text_color: root.theme_text_color
text_color: root.text_color
opposite_colors: root.opposite_colors
disabled: root.disabled
halign: 'center'
valign: 'middle'
<MDFloatingActionButton>:
canvas:
Clear
Color:
rgba: self.background_color_disabled if self.disabled else \
(self.background_color if self.state == 'normal' else self.background_color_down)
Ellipse:
size: self.size
pos: self.pos
anchor_x: 'center'
anchor_y: 'center'
background_color: root.theme_cls.accent_color
background_color_down: root.theme_cls.accent_dark
background_color_disabled: root.theme_cls.divider_color
theme_text_color: 'Primary'
MDLabel:
id: label
font_style: 'Icon'
text: u"{}".format(md_icons[root.icon])
size_hint: None, None
size: dp(24), dp(24)
text_size: self.size
theme_text_color: root.theme_text_color
text_color: root.text_color
opposite_colors: root.opposite_colors
disabled: root.disabled
halign: 'center'
valign: 'middle'
''')
class MDIconButton(CircularRippleBehavior, ButtonBehavior, BoxLayout):
icon = StringProperty('circle')
theme_text_color = OptionProperty(None, allownone=True,
options=['Primary', 'Secondary', 'Hint',
'Error', 'Custom'])
text_color = ListProperty(None, allownone=True)
opposite_colors = BooleanProperty(False)
class MDFlatButton(ThemableBehavior, RectangularRippleBehavior,
ButtonBehavior, BackgroundColorBehavior, AnchorLayout):
width = BoundedNumericProperty(dp(64), min=dp(64), max=None,
errorhandler=lambda x: dp(64))
text_color = ListProperty()
text = StringProperty('')
theme_text_color = OptionProperty(None, allownone=True,
options=['Primary', 'Secondary', 'Hint',
'Error', 'Custom'])
text_color = ListProperty(None, allownone=True)
_text = StringProperty('')
_bg_color_down = ListProperty([0, 0, 0, 0])
_current_button_color = ListProperty([0, 0, 0, 0])
def __init__(self, **kwargs):
super(MDFlatButton, self).__init__(**kwargs)
self._current_button_color = self.background_color
self._bg_color_down = get_color_from_hex(
colors[self.theme_cls.theme_style]['FlatButtonDown'])
Clock.schedule_once(lambda x: self.ids._label.bind(
texture_size=self.update_width_on_label_texture))
def update_width_on_label_texture(self, instance, value):
self.ids._label.width = value[0]
def on_text(self, instance, value):
self._text = value.upper()
def on_touch_down(self, touch):
if touch.is_mouse_scrolling:
return False
elif not self.collide_point(touch.x, touch.y):
return False
elif self in touch.ud:
return False
elif self.disabled:
return False
else:
self.fade_bg = Animation(duration=.2, _current_button_color=get_color_from_hex(
colors[self.theme_cls.theme_style]['FlatButtonDown']))
self.fade_bg.start(self)
return super(MDFlatButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
if touch.grab_current is self:
self.fade_bg.stop_property(self, '_current_button_color')
Animation(duration=.05, _current_button_color=self.background_color).start(self)
return super(MDFlatButton, self).on_touch_up(touch)
class MDRaisedButton(ThemableBehavior, RectangularRippleBehavior,
ElevationBehavior, ButtonBehavior,
AnchorLayout):
_bg_color_down = ListProperty([])
background_color = ListProperty()
background_color_down = ListProperty()
background_color_disabled = ListProperty()
theme_text_color = OptionProperty(None, allownone=True,
options=['Primary', 'Secondary', 'Hint',
'Error', 'Custom'])
text_color = ListProperty(None, allownone=True)
def _get_bg_color_down(self):
return self._bg_color_down
def _set_bg_color_down(self, color, alpha=None):
if len(color) == 2:
self._bg_color_down = get_color_from_hex(
colors[color[0]][color[1]])
if alpha:
self._bg_color_down[3] = alpha
elif len(color) == 4:
self._bg_color_down = color
background_color_down = AliasProperty(_get_bg_color_down,
_set_bg_color_down,
bind=('_bg_color_down',))
_bg_color_disabled = ListProperty([])
def _get_bg_color_disabled(self):
return self._bg_color_disabled
def _set_bg_color_disabled(self, color, alpha=None):
if len(color) == 2:
self._bg_color_disabled = get_color_from_hex(
colors[color[0]][color[1]])
if alpha:
self._bg_color_disabled[3] = alpha
elif len(color) == 4:
self._bg_color_disabled = color
background_color_disabled = AliasProperty(_get_bg_color_disabled,
_set_bg_color_disabled,
bind=('_bg_color_disabled',))
_elev_norm = NumericProperty(2)
def _get_elev_norm(self):
return self._elev_norm
def _set_elev_norm(self, value):
self._elev_norm = value if value <= 12 else 12
self._elev_raised = (value + 6) if value + 6 <= 12 else 12
self.elevation = self._elev_norm
elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm,
bind=('_elev_norm',))
_elev_raised = NumericProperty(8)
def _get_elev_raised(self):
return self._elev_raised
def _set_elev_raised(self, value):
self._elev_raised = value if value + self._elev_norm <= 12 else 12
elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised,
bind=('_elev_raised',))
text = StringProperty()
_text = StringProperty()
def __init__(self, **kwargs):
super(MDRaisedButton, self).__init__(**kwargs)
self.elevation_press_anim = Animation(elevation=self.elevation_raised,
duration=.2, t='out_quad')
self.elevation_release_anim = Animation(
elevation=self.elevation_normal, duration=.2, t='out_quad')
def on_disabled(self, instance, value):
if value:
self.elevation = 0
else:
self.elevation = self.elevation_normal
super(MDRaisedButton, self).on_disabled(instance, value)
def on_touch_down(self, touch):
if not self.disabled:
if touch.is_mouse_scrolling:
return False
if not self.collide_point(touch.x, touch.y):
return False
if self in touch.ud:
return False
Animation.cancel_all(self, 'elevation')
self.elevation_press_anim.start(self)
return super(MDRaisedButton, self).on_touch_down(touch)
def on_touch_up(self, touch):
if not self.disabled:
if touch.grab_current is not self:
return super(ButtonBehavior, self).on_touch_up(touch)
Animation.cancel_all(self, 'elevation')
self.elevation_release_anim.start(self)
else:
Animation.cancel_all(self, 'elevation')
self.elevation = 0
return super(MDRaisedButton, self).on_touch_up(touch)
def on_text(self, instance, text):
self._text = text.upper()
def on__elev_norm(self, instance, value):
self.elevation_release_anim = Animation(elevation=value,
duration=.2, t='out_quad')
def on__elev_raised(self, instance, value):
self.elevation_press_anim = Animation(elevation=value,
duration=.2, t='out_quad')
class MDFloatingActionButton(ThemableBehavior, CircularRippleBehavior,
RoundElevationBehavior, ButtonBehavior,
AnchorLayout):
_bg_color_down = ListProperty([])
background_color = ListProperty()
background_color_down = ListProperty()
background_color_disabled = ListProperty()
theme_text_color = OptionProperty(None, allownone=True,
options=['Primary', 'Secondary', 'Hint',
'Error', 'Custom'])
text_color = ListProperty(None, allownone=True)
def _get_bg_color_down(self):
return self._bg_color_down
def _set_bg_color_down(self, color, alpha=None):
if len(color) == 2:
self._bg_color_down = get_color_from_hex(
colors[color[0]][color[1]])
if alpha:
self._bg_color_down[3] = alpha
elif len(color) == 4:
self._bg_color_down = color
background_color_down = AliasProperty(_get_bg_color_down,
_set_bg_color_down,
bind=('_bg_color_down',))
_bg_color_disabled = ListProperty([])
def _get_bg_color_disabled(self):
return self._bg_color_disabled
def _set_bg_color_disabled(self, color, alpha=None):
if len(color) == 2:
self._bg_color_disabled = get_color_from_hex(
colors[color[0]][color[1]])
if alpha:
self._bg_color_disabled[3] = alpha
elif len(color) == 4:
self._bg_color_disabled = color
background_color_disabled = AliasProperty(_get_bg_color_disabled,
_set_bg_color_disabled,
bind=('_bg_color_disabled',))
icon = StringProperty('android')
_elev_norm = NumericProperty(6)
def _get_elev_norm(self):
return self._elev_norm
def _set_elev_norm(self, value):
self._elev_norm = value if value <= 12 else 12
self._elev_raised = (value + 6) if value + 6 <= 12 else 12
self.elevation = self._elev_norm
elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm,
bind=('_elev_norm',))
# _elev_raised = NumericProperty(12)
_elev_raised = NumericProperty(6)
def _get_elev_raised(self):
return self._elev_raised
def _set_elev_raised(self, value):
self._elev_raised = value if value + self._elev_norm <= 12 else 12
elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised,
bind=('_elev_raised',))
def __init__(self, **kwargs):
if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12:
self.elevation_raised = self.elevation_normal + 6
elif self.elevation_raised == 0:
self.elevation_raised = 12
super(MDFloatingActionButton, self).__init__(**kwargs)
self.elevation_press_anim = Animation(elevation=self.elevation_raised,
duration=.2, t='out_quad')
self.elevation_release_anim = Animation(
elevation=self.elevation_normal, duration=.2, t='out_quad')