I'm trying to grey out unavailable dates on the kivymd date picker (happy to use Kivy calendar if it's easier) and make it so either it doesn't let a user click a greyed out date or it will just bring up a popup saying this date is not available.
I know how to check if a date is unavailable to be selected, I can just check the date selected against a list containing unavailable dates and if the selection is in the list I can bring up a popup but I'm unsure how to do the design side - greying out the unavailable dates when a user opens the calendar.
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from kivymd.uix.picker import MDDatePicker
kv = """
<Pickers#Screen>
name: "pickers"
BoxLayout:
orientation: "vertical"
spacing: dp(20)
pos_hint: {"center_x": .5, "center_y": .5}
size_hint_y: None
height: self.minimum_height
MDRaisedButton:
text: "Open date picker"
pos_hint: {"center_x": .5}
opposite_colors: True
on_release: app.show_example_date_picker()
MDLabel:
id: date_picker_label
theme_text_color: "Primary"
halign: "center"
BoxLayout:
size_hint: None, None
size: self.minimum_size
pos_hint: {"center_x": .5}
Label:
theme_text_color: "Primary"
text: "Start on previous date"
size_hint_x: None
width: self.texture_size[0]
color: 0, 0, 0, 1
MDCheckbox:
id: date_picker_use_previous_date
size_hint: None, None
size: dp(48), dp(48)
"""
class MainApp(MDApp):
previous_date = ObjectProperty()
def __init__(self, **kwargs):
self.title = "KivyMD Examples - Date Picker"
super().__init__(**kwargs)
def build(self):
Builder.load_string(kv)
self.root = Factory.Pickers()
def show_example_date_picker(self, *args):
if self.root.ids.date_picker_use_previous_date.active:
pd = self.previous_date
try:
MDDatePicker(self.set_previous_date,
pd.year, pd.month, pd.day).open()
except AttributeError:
MDDatePicker(self.set_previous_date).open()
else:
MDDatePicker(self.set_previous_date).open()
def set_previous_date(self, date_obj):
self.previous_date = date_obj
self.root.ids.date_picker_label.text = str(date_obj)
if __name__ == "__main__":
MainApp().run()
You Can set the min and max date in the show_date_picker function.
Everything outside the range will be grayed out.
def show_date_picker(self):
min_date = datetime.strptime("2020:02:15", '%Y:%m:%d').date()
max_date = datetime.strptime("2020:05:30", '%Y:%m:%d').date()
date_dialog = MDDatePicker(
callback=self.get_date,
min_date=min_date,
max_date=max_date,
)
date_dialog.open()
Related
well I'm entirely new to kivy, it's a bit complicated but I'm trying to get on
so I'm stuck on something, I'm trying to get a text from a TextInput, but i can't, it just refuse to do it, can anyone crack it?
KV code:
<Signin>:
title:"Telegram Checker"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
BoxLayout:
width : 600
orientation: 'vertical'
size_hint: None, None
Label:
text:"Telegram Sign-In"
TextInput:
id : tel
input_type:'tel'
width:600
multiline: False
hint_text: '+XXXXXXXXXXXXX'
size_hint: None, None
height: 50
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
BoxLayout:
padding: 20
spacing: 100
width : 600
orientation: 'vertical'
size_hint: None, None
Button:
text: "Next ->"
#size_hint: 0.5,6
height : 100
pos:self.pos
on_release : app.send_code_request()
the function that's not getting the text :
class MyApp(App):
def build(self):
self.title="Telegram Checker"
return screen_manager
def send_code_request(self):
phone = self.root.ids.tel.text
print(phone)
The code at the bottom is runnable and it passes the argument you requested. It shows this being done two ways. I suggest not using the .ids property because you can link the items at the top of the .kv class and assign type hints to the object like this:
tel: TextInput
full code:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
Builder.load_string('''
<Signin>:
# match the python object to the kivy id
# python on the left: kivy id on the right
tel: tel
title:"Telegram Checker"
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
BoxLayout:
width : 600
orientation: 'vertical'
size_hint: None, None
Label:
text:"Telegram Sign-In"
TextInput:
id : tel
input_type:'tel'
width:600
multiline: False
hint_text: '+XXXXXXXXXXXXX'
size_hint: None, None
height: 50
AnchorLayout:
anchor_x: 'center'
anchor_y: 'bottom'
BoxLayout:
padding: 20
spacing: 100
width : 600
orientation: 'vertical'
size_hint: None, None
Button:
text: "Next ->"
#size_hint: 0.5,6
height : 100
pos:self.pos
on_release : app.send_code_request(tel.text, self)
'''
)
class Signin(Screen):
# python objects mapped in the .kv file. this is a type hint which will help with auto-complete
tel: TextInput
def __init__(self, **kwargs):
super().__init__(**kwargs)
print(f" hint text is {self.tel.hint_text}")
class MyApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.my_top_widget = ScreenManager()
self.signin_screen = Signin(name="sign_in")
def send_code_request(self, phone: str, your_button, *args) -> None:
# this is one way
print(f" by id {self.signin_screen.ids.tel.text}")
print(f"vale passed={phone} \nyour button {your_button} other args {args}")
def build(self):
self.title = "Telegram Checker"
self.my_top_widget.add_widget(self.signin_screen, name="sign_in")
self.my_top_widget.current = "sign_in"
return self.my_top_widget
my_app = MyApp()
my_app.run()
I want to switch from login screen to menu screen based on a successful authentication, but this is the best I could do after a long search on condition-based screen transitions. Most sites say that screen-transitions in kivymd should be done using 'on-release' in .kv file, but I don't think that it would work in my code.
I've marked on the code the problematic line, which is raising the exception.
Teste.py
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.dialog import MDDialog
from kivymd.uix.button import MDFlatButton
from kivy.uix.screenmanager import ScreenManager, Screen
class login(Screen):
pass
class menu(Screen):
pass
Builder.load_file('lteste.kv')
class LoginApp(MDApp):
dialog = None
def build(self): #método construtor da parte visual do aplicativo
self.theme_cls.theme_style = "Light"
self.theme_cls.primary_palette = "Indigo"
self.theme_cls.accent_palette = "Blue"
self.sm = ScreenManager()
self.sm.add_widget(login(name="login"))
self.sm.add_widget(menu(name="menu"))
self.sm.current = "menu"
return self.sm
def dialog_box(self):
if not self.dialog:
self.dialog = MDDialog(
title="Log In",
text=f"Welcome {self.root.ids.user.text}!",
buttons=[MDFlatButton(text="Ok", text_color=self.theme_cls.primary_color,
on_release=self.close),],)
return self.dialog.open()
def login(self):
if self.root.ids.user.text=='1' and self.root.ids.password.text=='1':
self.sm.current = "menu" #<- problem
self.dialog_box()
return True
else:
return False
def close(self, instance):
self.dialog.dismiss()
LoginApp().run()
lteste.kv
<login>:
id: login
name: "login"
MDCard:
size_hint: None, None
size: 300, 600
pos_hint: {"center_x": 0.5, "center_y": 0.5}
elevation: 10
padding: 65
spacing: 35
orientation: 'vertical'
MDIcon:
icon: 'account'
icon_color: 0, 0, 0, 0
halign: 'center'
font_size: 180
MDTextFieldRound:
id: user
icon_left: "account-check"
hint_text: "Usuário"
foreground_color: 1, 0, 1, 1
size_hint_x: None
width: 220
font_size: 20
pos_hint: {"center_x": 0.5}
MDTextFieldRound:
id: password
icon_left: "key-variant"
hint_text: "Senha"
foreground_color: 1, 0, 1, 1
size_hint_x: None
height: 1
width: 220
font_size: 20
pos_hint: {"center_x": 0.5}
password: True
MDFillRoundFlatButton:
text: "ENTRAR"
font_size: 15
pos_hint: {"center_x": 0.5}
on_press: app.login()
MDFillRoundFlatButton:
text: "REGISTRAR-SE"
font_size: 15
pos_hint: {"center_x": 0.5}
<menu>
name: "menu"
id: menu
MDCard:
size_hint: None, None
size: 300, 600
pos_hint: {"center_x": 0.5, "center_y": 0.5}
elevation: 10
padding: 65
spacing: 35
orientation: 'vertical'
MDRaisedButton:
text: "Test"
The application root does not contain the ids you want. They are in the login screen widget because you defined them under it in kivy language. It would help to store a reference on the login screen to access them. Replace
self.sm.add_widget(login(name="login"))
with
self.login_screen = login(name="login")
self.sm.add_widget(self.login_screen)
Then, you can access the widgets like so,
text=f"Welcome {self.login_screen.ids.user.text}!",
and
if self.login_screen.ids.user.text=='1' \
and self.login_screen.ids.password.text=='1':
There is an example in the Kivy Documentation on kivy.uix.widget.Widget.ids and also Accessing Widgets defined inside Kv lang in your Python code.
I'm creating an mp3 player using kivy recycleview, the app has a lot of buttons in the playlist screen and whenever you click on a button, icon of that button change from 'play' to 'pause' and vice versa.
I would like to know how to make it be in a way that clicking another button changes all the other buttons icon to 'play' only that selected button should be with icon of 'pause'.
.py file:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.core.window import Window
from kivy.properties import StringProperty, ObjectProperty
from kivymd.theming import ThemableBehavior
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.screen import MDScreen
from kivymd.uix.behaviors import RectangularRippleBehavior
from kivy.uix.behaviors import ButtonBehavior
from kivy.clock import Clock
Builder.load_file('playlist.kv')
KV = """
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
ScreenManager:
transition: FadeTransition()
Playlist:
name: "playlist screen"
"""
class Playlist(ThemableBehavior, MDScreen):
rv = ObjectProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self._finish_init)
def music_list(self):
return ['audio '+str(i) for i in range(1, 121)]
def _finish_init(self, dt):
self.set_list_musics()
def set_list_musics(self):
"""Builds a list of audios for the screen Playlist."""
print(self.ids)
def add_music_item(num, sura, secText, icon):
self.ids.rv.data.append(
{
"viewclass": "MusicListItem",
"number": num,
"text": sura,
"secondary_text": secText,
"icon": icon,
"callback": lambda x:x})
for i in range(len(self.music_list())):
music = self.music_list()
add_music_item(str(i+1), music[i], '00:00:00', 'play')
class MusicListItem(ThemableBehavior, RectangularRippleBehavior, ButtonBehavior, MDBoxLayout):
text = StringProperty()
secondary_text = StringProperty()
number = StringProperty()
icon = StringProperty()
def on_release(self, *args):
if self.icon == "play":
self.icon = "pause"
else:
self.icon = "play"
class Mp3Player(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
def build(self):
self.theme_cls.primary_palette = "Purple"
self.theme_cls.theme_style = "Dark"
return Builder.load_string(KV)
if '__main__' == __name__:
Mp3Player().run()
.kv file:
#: import gch kivy.utils.get_color_from_hex
#: import StiffScrollEffect kivymd.effects.stiffscroll.StiffScrollEffect
<Playlist>
md_bg_color: gch("#5D1049")
MDGridLayout:
cols: 1
MDToolbar:
left_action_items: [["menu", lambda x: x]]
right_action_items: [["magnify", lambda x: x]]
elevation: 10
md_bg_color: 75/255, 6/255, 54/255, 1
title: 'Playlist'
pos_hint: {'top':1}
MDBoxLayout:
orientation: 'vertical'
RecycleView:
id: rv
effect_cls: 'ScrollEffect'
viewclass: 'MusicListItem'
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(60)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<MusicListItem>
size_hint_y: None
padding: dp(14)
height: dp(60)
canvas:
Color:
rgba:
self.theme_cls.divider_color
Line:
points: (root.x+dp(10), root.y, root.x+self.width-dp(10)-0, root.y)
MDBoxLayout:
orientation: "horizontal"
pos_hint: {"center_x": .5, "center_y": .5}
MDBoxLayout:
orientation: 'horizontal'
MDBoxLayout:
orientation: 'vertical'
size_hint_x: .2
MDLabel:
text: root.number
font_style: "H6"
adaptive_height: True
MDLabel:
size_hint_y: .3
MDBoxLayout:
orientation: 'vertical'
MDLabel:
text: root.text
font_style: "Subtitle2"
adaptive_height: True
MDLabel:
text: root.secondary_text
font_style: "Caption"
theme_text_color: "Hint"
adaptive_height: True
MDIconButton:
icon: root.icon
Thank you
So, as I've understood, you want to set an icon as 'pause' while all other as 'play'. One way of doing this could be like, you have to reload the RecyclView data each time the icon changes.
Now to provide data with icon reference (i.e. 'play' or 'pause') I found the number property suitable, so I change it to NumericProperty. Thus number = NumericProperty().
Also this requires some change in kv,
MDLabel:
text: str(int(root.number))
font_style: "H6"
adaptive_height: True
To let Playlist know about the number reference,
def set_list_musics(self, music_no = 0):
"""Builds a list of audios for the screen Playlist."""
print(self.ids)
self.ids.rv.data = [ ] # Since you are appending data and we need to reload everytime.
Make required changes in data,
for i in range(len(self.music_list())):
new_icon = 'pause' if i+1 == music_no else 'play'
music = self.music_list()
add_music_item(str(i+1), music[i], '00:00:00', new_icon)
Now the final part, trigger the change via the button,
def on_release(self, *args):
if self.icon == "play":
self.icon = "pause"
pl = self.parent.parent.parent.parent.parent # Accessing the Playlist according to your design pattern.
pl.set_list_musics(self.number)
else:
self.icon = "play"
Note that I made this change in 'pause' icon (i.e. in if self.icon == "play"), thus you can also freely toggle this icon. Placing it otherwise could not make it possible.
Perhaps this could have been done more systematically with other design styles. I've found some issues with your design pattern. This (such as calling function in for loop repeatedly etc.) may make it a little bit slower as the data increases.
I'm making a kivy app to find the rhyming words for a word entered by the user. It displays all the rhyming words as OneLineListItems in an MDList which is inside a kivy RecycleView. On clicking on one of these OneLineListItems it displays the definition of the word on the right-hand side of the screen. However, when I click on a OneLineListItem its definition takes very long to appear and sometimes it lags so badly that the app closes. Am I doing something wrong or is it just my computer? Code below:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.label import MDLabel
from kivymd.uix.list import OneLineListItem
import pronouncing
import enchant
from PyDictionary import PyDictionary
dictionary = PyDictionary()
d = enchant.Dict("en_US")
kv = """
Screen:
input:input
scroll:scroll
word:word
defs:defs
MDGridLayout:
rows: 1
cols: 2
MDGridLayout:
rows: 2
cols: 1
MDFloatLayout:
MDTextField:
id:input
size_hint: (.4, None)
height: 26
multiline: False
on_text_validate: app.rhyme()
hint_text: "Search"
mode: "rectangle"
pos_hint: {"center_x": .25, "center_y": .85}
font_name: "DejaVuSans.ttf"
text_size: self.size
MDFloatLayout:
RecycleView:
size_hint: 0.85,1.5
bar_width: dp(15)
bar_height: dp(40)
scroll_type: ["content"]
pos_hint: {"center_x": 0.45, "center_y": 0.93}
MDList:
id: scroll
MDBoxLayout:
id:defs
orientation: "vertical"
md_bg_color: 0, 1, 0.2, 1
MDLabel:
id: word
text: ""
text_size: self.size
"""
class RhymeApp(MDApp):
played = []
x_turn = True
def build(self):
self.screen = Builder.load_string(kv)
return self.screen
def rhyme(self):
raw_rhymes = pronouncing.rhymes(self.screen.input.text)
rhymes = []
[rhymes.append(x) for x in raw_rhymes if x not in rhymes and x[-1] != "." and d.check(x)]
self.screen.scroll.clear_widgets()
for i in rhymes:
self.screen.scroll.add_widget(
OneLineListItem(text=f"{i}".capitalize(), on_release=self.dictionary)
)
def dictionary(self, btn):
nl = '\n'
self.screen.defs.clear_widgets()
self.screen.word.text = btn.text.capitalize()
meaning = dictionary.meaning(btn.text, disable_errors=True)
if meaning is None:
self.screen.defs.add_widget(
MDLabel(text=f"Sorry, no meaning for that word.",
text_size="self.size")
)
else:
for key in meaning:
self.screen.defs.add_widget(
MDLabel(text=f"Part of speech: {key} {nl}Meaning: {nl}{nl}{meaning[key][0].capitalize()}.",
text_size="self.size")
)
if __name__ == "__main__":
RhymeApp().run()
Can someone please help?
First create a custom class for the data-class like following:
class ListItem(OneLineListItem):
# Here define all the necessary attrs., methods apart from the defaults (if you need any).
Now in your .kv initialize RecycleView as,
RecycleView:
id: scroll
#size_hint: 0.85,1.5
bar_width: dp(15)
bar_height: dp(40)
scroll_type: ["content"]
#pos_hint: {"center_x": 0.45, "center_y": 0.93}
viewclass: "ListItem"
RecycleBoxLayout:
default_size: None, dp(48)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
Now you are ready to feed RecycleView with you data as,
def rhyme(self):
...
self.screen.ids.scroll.data = [
{"text" : f"{i}".capitalize()}
for i in rhymes]
I would like the user to be able to select multiple items from a list of check boxes, but for some reason checking a second box deselects the first and I cannot understand why. My code is below:
Python:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.dialog import MDDialog
from kivymd.uix.button import MDFlatButton
from kivymd.uix.list import ILeftBodyTouch
from kivymd.uix.selectioncontrol import MDCheckbox
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.properties import StringProperty
class LeftCheckbox(ILeftBodyTouch, MDCheckbox):
pass
class CreateWorkoutCustomDialog(MDBoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
lifts = ['Bench Press', 'Squat', 'Deadlift', 'Weighted Pull-ups', 'Rows', 'Shoulder Press']
for lift in lifts:
self.ids.box.add_widget(CreateWorkoutLiftRow(lift = lift))
class CreateWorkoutLiftRow(MDBoxLayout):
lift = StringProperty()
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.lift = kwargs['lift']
def set_icon(self, instance_check):
instance_check.active = True if instance_check.active == False else instance_check.active == False
class Main2App(MDApp):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.lift_dialog = None
def build(self):
Builder.load_file('dialog.kv')
return Builder.load_file("scratch.kv")
def show_lift_dialog(self):
if not self.lift_dialog:
self.lift_dialog = MDDialog(
title="Create New Workout",
type="custom",
content_cls = CreateWorkoutCustomDialog(),
buttons=[
MDFlatButton(text="CANCEL"),
MDFlatButton(text="OK"),
],
)
self.lift_dialog.open()
Main2App().run()
scratch.kv:
MDScreen:
MDFlatButton:
text: "ALERT DIALOG"
pos_hint: {'center_x': .5, 'center_y': .5}
on_release: app.show_lift_dialog()
dialog.kv:
#:kivy 2.0.0
<CreateWorkoutCustomDialog>:
orientation: 'vertical'
size_hint_y: None
height: '400dp'
MDTextField:
hint_text: "Workout Title"
required: True
id: workout_title
ScrollView:
MDBoxLayout:
orientation: 'vertical'
size_hint_y: None
height: self.minimum_height
id: box
spacing: "12dp"
<CreateWorkoutLiftRow>:
orientation: 'horizontal'
size_hint_y: None
height: self.minimum_height
ItemConfirm:
text: root.lift
on_release: root.set_icon(check)
size_hint_x: .7
divider: None
LeftCheckbox:
id: check
group: "check"
active: True
CreateWorkoutSetsInput:
id: input
hint_text: "# sets"
size_hint_x: .3
<ItemConfirm#OneLineAvatarIconListItem>:
<CreateWorkoutSetsInput#MDTextField>:
I've reviewed several tutorials and tried my best to understand the documentation but I cannot see where the interaction between discrete check boxes is even happening. Any help would be greatly appreciated!
That is the result of using group with the CheckBox. See the documentation.
group: "check"
Just remove that line in the kv.