I'm trying to make a book reader in python, and I want to put the covers in a gridlayout with 2 columns and make ir scrollable, but I'm having an issue. I've already seen some questions about ScrollView, but nothing worked for me until now.
Here is a piece of my code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.tab import MDTabsBase
class Library(Screen):
pass
class Book(Screen):
pass
class SM(ScreenManager):
pass
KV = '''
SM:
Library:
Book:
<Library>:
name: "lib"
MDBoxLayout:
orientation: "vertical"
MDToolbar:
id: t_b
title: "Library"
right_action_items: [["magnify", lambda x: x]]
MDTabs:
id: tabs
MDFloatLayout:
ScrollView:
pos_hint_y: None
pos_y: 0
size_hint_y: None
height: root.height
GridLayout:
cols: 2
spacing: "10dp"
size_hint_y: None
height: self.minimum_height
MDCard:
on_touch_down:
app.root.current = "book"
size_hint_y: None
height: "300dp"
Image:
source: "cover.png"
MDCard:
on_touch_down:
app.root.current = "book"
size_hint_y: None
height: "300dp"
Image:
source: "cover.png"
MDCard:
on_touch_down:
app.root.current = "book"
size_hint_y: None
height: "300dp"
Image:
source: "cover.png"
MDCard:
on_touch_down:
app.root.current = "book"
size_hint_y: None
height: "300dp"
Image:
source: "cover.png"
MDBottomNavigation:
MDBottomNavigationItem:
text: "Library"
icon: "book-variant-multiple"
MDBottomNavigationItem:
text: "History"
icon: "history"
MDBottomNavigationItem:
text: "Settings"
icon: "dots-horizontal"
<Book>:
name: "book"
'''
class Tab(MDFloatLayout, MDTabsBase):
pass
class Aplication(MDApp):
def build(self):
screen = Builder.load_string(KV)
return screen
def on_start(self):
tags = ['Action', 'Romance', 'Horror', 'Adventure']
for tag in tags:
self.root.get_screen("lib").ids.tabs.add_widget(Tab(title=tag))
if __name__ == "__main__":
Aplication().run()
It makes a ScrollView with the cards, but it's overlaying the toolbar and the tabs. What should I do?
Related
I'm new working with kivymd and I need to generate an app with two screens, the second screen shows a TwoLineListItem inside a recycleview, I need to bind a function that takes the text on the selected row a put it in a text field of the main window, but I'm getting this error:
TypeError: click_supplier() missing 1 required positional argument: 'supplier_list_item'
Here's a similar example of my code:
PY
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.list import OneLineListItem, TwoLineListItem
from kivy.uix.recycleview import RecycleView
from kivy.metrics import dp
suppliers=['Amazon', 'Ebay', 'Alibaba', 'Linio', 'Aliexpress']
suppliers_id=['198', '283', '343', '454', '203']
class MainWindow(Screen):
pass
class SelectSupplierWindow(Screen):
pass
class BrisaWindowManager(ScreenManager):
pass
class MainApp(MDApp):
#define build parameters
product_dialog=None
selected_products_list=[]
def build(self):
self.theme_cls.theme_style="Light"
self.theme_cls.primary_palette="Green"
return Builder.load_file('example.kv')
def go_to_screen(self, screen):
self.root.current=screen
#start suppliers list
def on_start(self):
self.set_list_of_suppliers()
#Autofill supplier search field--------------------------------------------------
def set_list_of_suppliers(self, text="", search=False):
def add_supplier_item(name_supplier):
self.root.get_screen('select_supplier').ids.suppliers_rv.data.append(
{
"viewclass": "TwoLineListItem",
"text": name_supplier,
"secondary_text": f'Code: {suppliers_id[suppliers.index(name_supplier)]}',
"on_release": self.click_supplier,
}
)
self.root.get_screen('select_supplier').ids.suppliers_rv.data = []
for name_supplier in suppliers:
if search:
if text.lower() in name_supplier.lower():
add_supplier_item(name_supplier)
else:
add_supplier_item(name_supplier)
def click_supplier(self, supplier_list_item):
self.root.get_screen('main').ids.oc_supplier.text=supplier_list_item.text
self.go_to_screen('main')
MainApp().run()
KV
BrisaWindowManager:
MainWindow:
id:main_window
SelectSupplierWindow:
<MainWindow>
name: 'main'
MDBoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Brisa'
MDBottomNavigation:
MDBottomNavigationItem:
id: oc_screen
name: 'screen 1'
text: 'Purchase'
icon: 'clipboard-list'
MDBoxLayout:
orientation: 'vertical'
padding:dp(20)
spacing: dp(15)
MDTextField:
id: oc_supplier
hint_text: 'Select'
helper_text: 'Select'
helper_text_mode: 'on_focus'
mode: 'fill'
halign:'center'
on_focus:
app.root.current='select_supplier'
root.manager.transition.direction='up'
MDBottomNavigationItem:
name: 'screen 2'
text: 'Check'
icon: 'truck-check'
<SelectSupplierWindow>
name:'select_supplier'
MDBoxLayout:
orientation: 'vertical'
MDToolbar:
title: 'Select'
left_action_items: [['arrow-left', lambda x: app.go_to_screen(screen='main'), 'back']]
MDBoxLayout:
orientation:'vertical'
spacing: dp(10)
padding: dp(20)
MDBoxLayout:
adaptive_height:True
MDIconButton:
icon: 'magnify'
MDTextField:
id: search_field
on_text: app.set_list_of_suppliers(self.text, True)
RecycleView:
id: suppliers_rv
key_viewclass: 'viewclass'
key_size: 'height'
RecycleBoxLayout:
padding: dp(10)
default_size: None, dp(60)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
The error triggered because the click item function needs the clicked row but I haven't been able to bind this event to the function.
How about changing your add_supplier_item() method to:
def add_supplier_item(name_supplier):
self.root.get_screen('select_supplier').ids.suppliers_rv.data.append(
{
"viewclass": "TwoLineListItem",
"text": name_supplier,
"secondary_text": f'Code: {suppliers_id[suppliers.index(name_supplier)]}',
"on_release": partial(self.click_supplier, name_supplier),
}
)
And your click_supplier() method to:
def click_supplier(self, supplier_list_item):
self.root.get_screen('main').ids.oc_supplier.text=supplier_list_item
self.go_to_screen('main')
Rather than trying to pass the item, just pass the text.
i am trying to set the font for the Arabic language in kivymd for menu items in kivymd, I didn't find any solution for this after reviewing the docs well, so does anyone has an idea?
Use my example code, for this code to work you must download a ttf font file that contains the Arabic language characters and save it into the same folder where you're executing the code below, I downloaded JannaLTBold.ttf:
Here is the code by jbsidis:
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.list import OneLineListItem
from kivymd.uix.card import MDCard
KV = '''
Screen:
BoxLayout:
id: m5
spacing: dp(10)
#padding: dp(20)
pos_hint: {"center_x": .5, "center_y": .55}
orientation: "vertical"
BoxLayout:
size_hint_y: None
height: self.minimum_height
pos_hint: {"center_x": .5, "center_y": .4}
orientation: "vertical"
ScrollView:
id: rvvv9
bar_color: [0,0,1,.5]
bar_inactive_color: [1,1,1,0]
bar_width: dp(5)
BoxLayout:
id: container
padding: dp(10)
spacing: dp(100)
default_size: None, dp(75)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
<NN>:
GridLayout:
cols: 2
spacing: dp(30)
OneLineListItem:
markup: True
text: "[font=JannaLTBold.ttf] jbsidis شخص في هذا العالم يسمى: جبسيديس"
MDCard:
size_hint: 1,None
Image:
source: "Photos/pro.jpg"
'''
class NN(MDCard):
pass
class Test(MDApp):
def build(self):
return Builder.load_string(KV)
def on_start(self):
for i in range(20):
self.root.ids.container.add_widget(
NN()
)
Test().run()
The result should be like the image:
jbsidis
I have this python file
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.theming import ThemableBehavior, ThemeManager
from kivymd.uix.list import OneLineIconListItem, MDList
class ContentNavigationDrawer(BoxLayout):
pass
class ItemDrawer(OneLineIconListItem):
icon = StringProperty()
class MainApp(MDApp):
def on_start(self):
icons_item = {
'bug': 'Files'
}
for icon_name in icons_item.keys():
self.root.ids.content_drawer.ids.md_list.add_widget(
ItemDrawer(icon=icon_name, text=icons_item[icon_name])
)
class DrawerList(ThemableBehavior, MDList):
def set_color_item(self, instance_item):
# Set the color of the icon and text for the menu item.
for item in self.children:
if item.text_color == self.theme_cls.primary_color:
item.text_color = self.theme_cls.text_color
break
instance_item.text_color = self.theme_cls.primary_color
MainApp().run()
And this .kv file
See Line 3, the error, comes there that screen_manager is not defined
<ItemDrawer>:
theme_text_color: "Custom"
on_release: screen_manager.current = 'screen2'
IconLeftWidget:
id: icon
icon: root.icon
theme_text_color: "Custom"
text_color: 0,0,0,1
<ContentNavigationDrawer>:
orientation: "vertical"
padding: "8dp"
spacing: "8dp"
AnchorLayout:
anchor_x: "left"
size_hint_y: None
height: avatar.height
Image:
id: avatar
size_hint: None, None
size: "250", "250"
source: "logo.jpg"
ScrollView:
DrawerList:
id: md_list
NavigationLayout:
ScreenManager:
id: screen_manager
Screen:
name: 'screen1'
BoxLayout:
orientation: 'vertical'
Widget:
canvas:
Color:
rgba: 0,1,0.2117647058823529,0.3
Rectangle:
size: self.size
pos: self.pos
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
id: content_drawer
Screen:
name: 'screen2'
When I click on the item in the navigation drawer, it says that screen_manager is not defined
What am I doing wrong? I searched the internet a lot but couldnot find anything useful
Help me please, how do i change screens?
The MDNavigation Drawer should be at the same indent level as the ScreenManager. Furthermore,
MDNavigationLayout:
screen_manager:screen_manager
ScreenManager:
id: screen_manager
Screen:
name: 'screen1'
BoxLayout:
orientation: 'vertical'
MDToolbar:
title: "Navigation Drawer"
elevation: 10
left_action_items: [['menu', lambda x:nav_drawer.set_state("open")]]
Widget:
Screen:
name: 'screen2'
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
id: content_drawer
add the
screen_manager:screen_manager
beneath the NavigationLayout so that it can be accessed as an ObjectProperty. Finally change the ItemDrawer callback from
on_release: screen_manager.current = 'screen2'
to
on_release: app.root.screen_manager.current = 'screen2'
Finally you can also add
nav_drawer: nav_drawer
beneath your root window and just ontop/below the line screen_manager: screen_manager
This enables for dismissing of the navigation drawer when the new screen pops up.
then update your callback to this:
on_release:
app.root.screen_manager.current = 'screen2'
app.root.nav_drawer.set_state('close')
I am trying to add an action bar on top in the first screen of my project. I tried using screenmanager widget and sending the action bar as it's children like how to manage/get both of the screens. At first I tried just adding the action bar code in root.widget in the first screen, but they are showing the class for this as an invalid class.
How to add both of them? Also I can't show the buttons from top to bottom even though I added orientation : 'vertical'
import kivy
kivy.require('1.10.1')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager,Screen,FadeTransition
class SomeLayout_GridLayout(Screen):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
root_widget = Builder.load_string('''
ScreenManager:
FirstScreen:
SecondScreen:
SomeLayout_GridLayout:
<FirstScreen>:
name: 'first'
<SomeLayout_GridLayout>:
cols: 1
rows: 2
row_force_default: True
rows_minimum: {0: ActionBar.height, 1: self.height - ActionBar.height}
SomeMenu_ActionBar:
id: ActionBar
<SomeMenu_ActionBar#ActionBar>:
ActionView:
id: ActionView
ActionGroup:
id: App_ActionGroup
mode: 'spinner'
text: 'App'
ActionButton:
text: 'Settings'
on_press: app.open_settings()
ActionButton:
text: 'Quit'
on_press: app.get_running_app().stop()
ActionGroup:
id: File_ActionGroup
mode: 'spinner'
text: 'File'
ActionButton:
text: 'Open'
ActionButton:
text: 'Save'
<HiddenIcon_ActionPrevious#ActionPrevious>:
title: app.title if app.title is not None else 'Action Previous'
with_previous: False
app_icon: ''
app_icon_width: 0
app_icon_height: 0
size_hint_x: None
width: len(self.title) * 10
<HiddenText_ActionPrevious#ActionPrevious>: #
with_previous: False
on_press: print(self)
title: ''
<Hidden_ActionPrevious#ActionPrevious>:
with_previous: False
on_press: print(self)
title: ''
size_hint: None, None
size: 0, 0
BoxLayout:
orientation: 'horizontal'
BoxLayout:
Button:
text: 'Crime Prediction'
font_size: 30
on_release: app.root.current = 'second'
Button:
text: 'Forum'
font_size: 30
on_release: app.root.current = 'second'
Button:
text: 'Probable Suspect'
font_size: 30
on_release: app.root.current = 'second'
<SecondScreen>:
name: 'second'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Predict Crime Nigga!'
font_size: 50
BoxLayout:`enter code here`
Button:
text: 'Back to Main Menu'
font_size: 30
on_release: app.root.current = 'first'
Button:
text: 'get random colour screen'
font_size: 30
on_release: app.root.current = 'first'
''')
class ScreenManagerApp(App):
def build(self):
return root_widget
ScreenManagerApp().run()
Kivy App with ActionBar & ScreenManager
Declare a root widget with inheritance of BoxLayout
Add ActionBar as child of root widget
Add ScreenManager as child of root widget, and with id: sm
Snippets
BoxLayout:
orientation: 'vertical'
ActionBar:
...
ScreenManager:
id: sm
FirstScreen:
SecondScreen:
Example
main.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
class WelcomeScreen(Screen):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
class ScreenManager(ScreenManager):
pass
class CrimePrevention(BoxLayout):
pass
Builder.load_file("main.kv")
class TestApp(App):
title = 'Kivy ScreenManager & ActionBar Demo'
def build(self):
return CrimePrevention()
if __name__ == '__main__':
TestApp().run()
main.kv
#:kivy 1.11.0
#:import sp kivy.metrics.sp
#:import dp kivy.metrics.dp
<CrimePrevention>:
orientation: 'vertical'
canvas.before:
Color:
rgb: .6, .6, .6
Rectangle:
pos: self.pos
size: self.size
# source: 'data/background.png'
SomeMenu_ActionBar:
id: ActionBar
ScreenManager:
id: sm
WelcomeScreen:
FirstScreen:
SecondScreen:
<SomeMenu_ActionBar#ActionBar>:
ActionView:
id: ActionView
HiddenIcon_ActionPrevious:
ActionGroup:
id: App_ActionGroup
mode: 'spinner'
text: 'Jump to Screen'
ActionButton:
text: 'Crime Prediction'
on_release: app.root.ids.sm.current = 'second'
ActionButton:
text: 'Forum'
on_release: app.root.ids.sm.current = 'second'
ActionButton:
text: 'Probable Suspect'
on_release: app.root.ids.sm.current = 'second'
ActionGroup:
id: App_ActionGroup
mode: 'spinner'
text: 'App'
ActionButton:
text: 'Settings'
on_press: app.open_settings()
ActionButton:
text: 'Quit'
on_press: app.get_running_app().stop()
ActionGroup:
id: File_ActionGroup
mode: 'spinner'
text: 'File'
ActionButton:
text: 'Open'
ActionButton:
text: 'Save'
<HiddenIcon_ActionPrevious#ActionPrevious>:
title: '' # app.title if app.title is not None else 'Action Previous'
with_previous: False
app_icon: ''
app_icon_width: 0
app_icon_height: 0
size_hint_x: None
width: len(self.title) * 10
<WelcomeScreen>:
name: 'welcome'
Label:
text: 'Welcome Screen'
font_size: sp(50)
<FirstScreen>:
name: 'first'
Label:
text: 'First Screen'
<SecondScreen>:
name: 'second'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Predict Crime'
font_size: 50
BoxLayout:
Button:
text: 'Back to Main Menu'
font_size: 30
on_release: app.root.ids.sm.current = 'first'
Button:
text: 'get random colour screen'
font_size: 30
on_release: app.root.ids.sm.current = 'first'
Output
I'm currently trying to create a simple Slider that will control the size of text in my application. The problem I'm running into though is that even though the slider functions as I intended, it seems to create another version of itself beneath the first slider that can't be moved. You can see how it looks in the provided image here [alt text: a screenshot of a rudimentary user interface showing a kivy slider. The slider has been moved forward behind it there is another copy in the default position] (as you can see the Label text inside the BoxLayout is also getting overlapped). I'm currently using Kivy 1.10.1 and Python 3.7.2.
Here is my Python script:
# -*- coding: utf-8 -*-
import kivy
kivy.require('1.10.1')
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.uix.actionbar import ActionBar
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.graphics.vertex_instructions import (Rectangle, Ellipse, Line)
from kivy.graphics.context_instructions import Color
from kivy.uix.checkbox import CheckBox
from kivy.uix.slider import Slider
#Window.size = (360/1.2,740/1.2)
class HomeScreen(Screen):
pass
class OptionsScreen(Screen):
pass
class TutorialScreen(Screen):
pass
class ScreenController(ScreenManager):
pass
look = Builder.load_file('main.kv')
class MainApp(App):
def build(self):
return look
if __name__ == '__main__':
MainApp().run()
And here is the relevant piece of Kivy language meant to create the slider on my
<OptionsScreen>
name: 'option'
BoxLayout:
orientation:'vertical'
BoxLayout:
orientation:'horizontal'
size_hint_y: 1/3
Label:
text:'Text size'
font_size: textsize.value
size_hint_x:.5
Slider:
id:textsize
min: 5
max: 25
value:15
step: 1
size_hint_x:.5
Though if you want to read the entire Kivy language document I'm posting it here as well.
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
# Reference main.py
#: import main main
#: import Slider kivy.uix.slider
#: import ActionBar kivy.uix.actionbar
#: import Window kivy.core.window
ScreenController:
transition: FadeTransition()
HomeScreen:
OptionsScreen:
TutorialScreen:
<HomeScreen>
name: 'home'
BoxLayout:
id:'hem'
orientation:'vertical'
BoxLayout:
size_hint_x: 1
orientation:'horizontal'
canvas:
Color:
rgba: 0,1,0,1
Rectangle
size: self.size
pos: self.pos
BoxLayout:
orientation:'horizontal'
BoxLayout:
size_hint_x: .5
orientation:'horizontal'
canvas:
Color:
rgba: 1,0,1,1
Rectangle
size: self.size
pos: self.pos
BoxLayout:
size_hint_x: .5
orientation:'horizontal'
canvas:
Color:
rgba: 1,1,0,1
Rectangle
size: self.size
pos: self.pos
ActionBar:
pos_hint: {'top':1}
ActionView:
use_separator: True
ActionPrevious:
title: 'Fredde & Kribbas kivy'
with_previous: False
ActionOverflow:
ActionGroup:
text: 'Group1'
ActionButton:
text: 'home'
on_touch_down: app.root.current = 'home'
ActionButton:
text: 'Options'
on_touch_down: app.root.current = 'option'
ActionButton:
text: 'Tutorial'
on_touch_down: app.root.current = 'tut'
<OptionsScreen>
name: 'option'
BoxLayout:
orientation:'vertical'
BoxLayout:
orientation:'horizontal'
size_hint_y: 1/3
Label:
text:'Text size'
font_size: textsize.value
size_hint_x:.5
Slider:
id:textsize
min: 5
max: 25
value:15
step: 1
size_hint_x:.5
BoxLayout:
#fontsize checkbox
orientation:'horizontal'
size_hint_y: 1/3
BoxLayout:
orientation:'vertical'
Label:
text: 'Nightmode'
CheckBox:
id:default
size_hint_y: None
active: True
height:'50dp'
group:'g1'
BoxLayout:
orientation:'vertical'
Label:
text: 'Daymode'
CheckBox:
id:stor
size_hint_y: None
height:'50dp'
group:'g1'
BoxLayout:
orientation:'horizontal'
size_hint_y: 1/3
canvas:
Color:
rgba: 0,0,1,1
Rectangle
size: self.size
pos: self.pos
ActionBar:
pos_hint: {'top':1}
ActionView:
use_separator: True
ActionPrevious:
title: 'Fredde & Kribbas kivy'
with_previous: False
ActionOverflow:
ActionGroup:
text: 'Group1'
ActionButton:
text: 'home'
on_touch_down: app.root.current = 'home'
ActionButton:
text: 'Options'
on_touch_down: app.root.current = 'option'
ActionButton:
text: 'Tutorial'
on_touch_down: app.root.current = 'tut'
<TutorialScreen>
name: 'tut'
ActionBar:
pos_hint: {'top':1}
ActionView:
use_separator: True
ActionPrevious:
title: 'Fredde & Kribbas kivy'
with_previous: False
ActionOverflow:
ActionGroup:
text: 'Group1'
ActionButton:
text: 'home'
on_touch_down: app.root.current = 'home'
ActionButton:
text: 'Options'
on_touch_down: app.root.current = 'option'
ActionButton:
text: 'Tutorial'
on_touch_down: app.root.current = 'tut'
Does anyone know why I might be having this issue? I'm fairly new to kivy but I've played around with the slider before and never had these issues until now.
You don't need to load your kv file inside your python code since you already named your kv file as main.kv.
You can try to remove this line:
look = Builder.load_file('main.kv')
and change this line:
return look
to:
pass