Clock warning when using MDRectangleFlatButton and MDRaisedButton - python

I am making a desktop application using kivy & kivymd and when creating the settings screen for the app I used two MDRectangleFlatButon and MDRaisedButtons but to make them scalable, I gave both of them a size_hint_x of .5 for each to take up half of the screen. However, as soon as the program starts I get this warning from kivy:
[CRITICAL] [Clock ] Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute
the interesting part is that if I switch the current screen to the one that I defined them inside of it, the warning stops. Why am I getting this warning and how can I stop it? My python file:
from kivy.config import Config
Config.set('graphics', 'width', '850')
Config.set('graphics', 'height', '530')
Config.set('graphics', 'minimum_width', '850')
Config.set('graphics', 'minimum_height', '530')
from kivy.lang import Builder
from kivymd.uix.card import MDCard
from kivymd.uix.tab import MDTabsBase
from kivymd.app import MDApp
class SettingsTab(MDCard, MDTabsBase):
pass
class Example(MDApp):
def __init__(self, **kwargs):
super(Example, self).__init__(**kwargs)
self.kv = Builder.load_file("design.kv")
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Blue"
self.theme_cls.accent_palette = "Teal"
return self.kv
if __name__ == '__main__':
Example().run()
My kv file:
#:kivy 2.0.0
<SettingsTab>:
orientation: "vertical"
size_hint: .95, .95
pos_hint: {"center_x": .5, "center_y": .5}
border_radius: 5
radius: [5]
elevation: 20
BoxLayout:
MDNavigationRail:
color_active: app.theme_cls.primary_color
MDNavigationRailItem:
icon: "list-status"
on_release:
screens.current = "downloads_screen"
MDNavigationRailItem:
icon: "cog"
on_release:
screens.current = "settings"
MDNavigationRailItem:
icon: "information"
on_release:
screens.current = "activity_log"
ScreenManager:
id: screens
Screen:
name: "downloads_screen"
Screen:
name: "activity_log"
Screen:
name: "settings"
MDTabs:
SettingsTab:
title: "DOWNLOAD"
SettingsTab:
title: "COLOR"
MDBoxLayout:
adaptive_height: True
orientation: "vertical"
padding: 10
MDLabel:
text: "Theme Manager: "
font_style: "H5"
padding_y: 10
size_hint_y: None
height: self.texture_size[1]
MDBoxLayout:
adaptive_height: True
spacing: 10
MDRectangleFlatButton:
text: "See Current Palette"
size_hint_x: .5
MDRaisedButton:
text: "Open Theme Picker"
size_hint_x: .5
SettingsTab:
title: "INFO"
EDIT: Just to mention, I do not just want the warning go away, I want to stop the performance drop. Even if I didn't add size_hint to any of the buttons (which doesn't cause the error anymore), the app becomes very sluggish and slow. When resizing the program there is a huge delay and when the program starts it is really doing a number on my cpu and it won't change until I switch to that frame.

If you change the order of your Screens definition, so that the "settings" Screen is the first listed under ScreenManager in your kv, then the error messages go away. Then adding to the Example class definition:
def on_start(self):
self.kv.ids.screens.transition = NoTransition()
self.kv.ids.screens.current = 'downloads_screen'
self.kv.ids.screens.transition = SlideTransition()
makes the downloads_screen appear first.

Related

Kivy scrollview with screenmanager and gridlayout

Like many other questions, im having issues with scrollview.
I'm trying to have a bunch of different buttons, that goes to different screens, but the buttons sit on top of each other, are stuck in the corner or only one button is appearing and the screen is cut in half.
I've tried having the screenmanager outside the TabbedPanelItem, giving every widget a size and no size_hint_y but to no avail? is it not possible to have the screenmanager inside the tabbedpanel or is it bad practice
Python file.
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.config import Config
from kivy.lang import Builder
Config.set('graphics', 'resizeable', 0)
Window.size = (375,700)
class MainScreen(Screen):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
root = Builder.load_file("main.kv")
class myapp(App):
def build(self):
return root
if __name__ == '__main__':
myapp().run()
main.kv
TabbedPanel:
do_default_tab: False
TabbedPanelItem:
text:"1"
ScrollView:
size: self.size
FloatLayout: #gridlayout? #boxlayout?
size_hint_y: None
ScreenManager:
id: manager
MainScreen:
name: 'main'
GridLayout:
spacing: 10
padding: 10
cols:1
row_default_height: 100
row_force_default: True
Button:
text: 'first'
on_press: manager.current = 'first'
size_hint: 1, None
height: 100
Button:
text: 'Second'
on_press: manager.current = 'second'
FirstScreen:
name: 'first'
SecondScreen:
name: "second"
TabbedPanelItem:
text:"2"
TabbedPanelItem:
text:"3"
<FirstScreen>:
BoxLayout:
Label:
text:"first"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
<SecondScreen>
BoxLayout:
Label:
text:"second"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
You can do what you want, but it seems a bit complicated. Here is a kv that I think will do it:
TabbedPanel:
do_default_tab: False
TabbedPanelItem:
text:"1"
ScrollView:
ScreenManager:
id: manager
size_hint_y: None
height: self.current_screen.height # only seems to work for first Screen
on_current_screen: self.height = self.current_screen.height # required because above line failure
MainScreen:
name: 'main'
size_hint_y: None
height: grid.height # set this Screen height to the height of the GridLayout
GridLayout:
id: grid
size_hint_y: None
height: self.minimum_height # set this height to the minimum required
spacing: 10
padding: 10
cols:1
row_default_height: 100
row_force_default: True
Button:
text: 'first'
on_press: manager.current = 'first'
size_hint: 1, None
height: 100
Button:
text: 'Second'
on_press: manager.current = 'second'
FirstScreen:
name: 'first'
SecondScreen:
name: "second"
TabbedPanelItem:
text:"2"
TabbedPanelItem:
text:"3"
<FirstScreen>:
size_hint_y: None
height: 100 # must set its height
BoxLayout:
Label:
text:"first"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
<SecondScreen>
size_hint_y: None
height: 100 # must set its height
BoxLayout:
Label:
text:"second"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
A few things to note:
You don't need a Layout to contain the ScreenManager.
You need to set the height of the ScreenManager based on the height of its current Screen.
The line height: self.current_screen.height should adjust the height of the ScreenManager every time the current Screen changes, but it doesn't. So I added on_current_screen: self.height = self.current_screen.height to adjust the size when the current Screen changes.
Since the ScreenManager height depends on the Screen height, every Screen must have a well defined height.
The MainScreen height is determined by the GridLayout height, and the GridLayout height is calculated using self.minimum_height.

How do I pass an argument between KivyMD screens?

When going from the first screen to the second screen, I want to pass a variable as an argument so that kivyMD can update the second screen from text stored in an excel file. The following is a skeleton of my app's functionality:
The user reaches Screen 1 thru the navigation drawer in KivyMD, screen 1 presents the user with two options on two small clickable MDCards:
"Change text to 1"
"Change text to 2"
After clicking on one of these, the app switches to screen 2 with a single big MDCard, the text on this MDCard should change to reflect the option the user chose.
However, kivy is pulling the text that is to be displayed on the big MDCard from an excel file.
The variable that I want to pass from screen 1 to screen 2 is simply a number (1 or 2) that will tell kivy which row in the excel file it should pull the text from
If the user clicks "Change text to 1" then the first screen should pass "1" as the argument row_x to the function def change_text() (see screen 2 .py) so that the text in row 1 of excel can be displayed on the second screen. How can I achieve this?
I have 4 files in total; 3 are .py files (one for the main app, one for screen 1, and one for screen 2), and the excel file
NOTE: in the code below, Screen 1 & 2 are called Element 1 & 2 respectfully
Main.py:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivymd.app import MDApp
from element_1 import element_1_screen
from element_2 import element_2_screen
MainNavigation = '''
<ContentNavigationDrawer>:
ScrollView:
MDList:
OneLineListItem:
text: 'Go to Element 1'
on_press:
root.nav_drawer.set_state("close")
root.screen_manager.current = "go_to_element_1_screen"
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
MDNavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
Screen:
name: "words_nav_item"
element_1_screen:
name: "go_to_element_1_screen"
element_2_screen:
name: "go_to_element_2_screen"
MDNavigationDrawer:
id: nav_drawer
ContentNavigationDrawer:
screen_manager: screen_manager
nav_drawer: nav_drawer
'''
class ContentNavigationDrawer(BoxLayout):
screen_manager = ObjectProperty()
nav_drawer = ObjectProperty()
class mainApp(MDApp):
def build(self):
self.theme_cls.primary_palette = "Red"
return Builder.load_string(MainNavigation)
mainApp().run()
Screen 1 / Element 1
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
element_1_contents = '''
<element_1_screen>:
MDGridLayout:
rows: 2
size: root.width, root.height
pos_hint: {"center_x": .8, "center_y": .2}
spacing: 40
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "120dp"
ripple_behavior: True
on_release:
root.manager.current = "go_to_element_2_screen"
MDLabel:
id: LabelTextID
text: "Change Text to 1"
halign: 'center'
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "120dp"
ripple_behavior: True
on_release:
root.manager.current = "go_to_element_2_screen"
MDLabel:
id: LabelTextID
text: "Change Text to 2"
halign: 'center'
'''
class element_1_screen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Builder.load_string(element_1_contents)
Screen 2 / Element 2
from kivy.lang import Builder
from kivymd.uix.screen import MDScreen
import openpyxl
element_2_contents = '''
<element_2_screen>:
MDCard:
orientation: 'vertical'
size_hint: None, None
size: "360dp", "360dp"
pos_hint: {"center_x": .5, "center_y": .5}
ripple_behavior: True
focus_behavior: True
on_release: root.manager.current = "go_to_element_1_screen"
MDLabel:
id: TextID
text: "NOTHING HAS CHANGED"
halign: 'center'
MDLabel:
text: "(Click here to return)"
halign: 'center'
'''
class element_2_screen(MDScreen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
path = "data.xlsx"
self.wb_obj = openpyxl.load_workbook(path)
self.sheet_obj = self.wb_obj.active
Builder.load_string(element_2_contents)
def change_text(self, row_x=0):
row_number = self.sheet_obj.cell(row_x, column=1)
self.ids.TextID.text = str(row_number.value)
And the excel file only has two entries in Column A:
Row 1: You have chosen 1
Row 2: You have chosen 2
I found the answer and now it works flawlessly. Someone over on Reddit (u/Username_RANDINT) helped me, this is what they said:
The ScreenManager has a get_screen() method. You could use it to get
the instance of the second screen and call the change_text() method on
that. In the same place where you switch screens, add another line:
on_release:
root.manager.current = "go_to_element_2_screen"
root.manager.get_screen("go_to_element_2_screen").change_text(1)
Then the same for the other card, just pass in 2 instead of 1.

impossible to get screen of kivy app after .build() and .start()

This code works well, I can initiate the application with a dynamique number of screens and update the menu list in consequence.
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.list import OneLineListItem
KV = '''
Screen:
MDToolbar:
id: toolbar
pos_hint: {"top": 1}
elevation: 10
title: "MDNavigationDrawer"
left_action_items: [["menu", lambda x: nav_drawer.set_state("open")]]
NavigationLayout:
x: toolbar.height
ScreenManager:
id: screen_manager
MDNavigationDrawer:
id: nav_drawer
BoxLayout:
canvas:
Rectangle:
pos: self.pos
size: self.size
orientation: 'vertical'
padding: "3dp"
spacing: "8dp"
AnchorLayout:
anchor_x: "center"
size_hint:1, None
halign: "center"
height: img_baniere_avekia_top.height
Image:
id: img_baniere_avekia_top
size_hint: None, None
size: "150dp", "93.56dp"
source: "menu_image.png"
ScrollView:
MDList:
id: menu_list
'''
screen_kv = '''
Screen:
MDLabel:
text: "Screen 1"
halign: "center"
'''
def chose_screen(widget, nav_drawer, screen_manager, text):
nav_drawer.set_state("close")
screen_manager.current = text
def add_tab(nav_drawer, screen_manager, menu_list, screen, title):
screen.name = title
menu_element = OneLineListItem(text=title)
menu_list.add_widget(menu_element)
screen_manager.add_widget(screen)
menu_element.bind(on_press = lambda w:chose_screen(w, nav_drawer, screen_manager, title))
class App(MDApp):
def build(self):
print("build app")
self.screen = Builder.load_string(KV)
return self.screen
def on_start(self):
print("start app")
for title in ["general","info"]:
self.add_tab(Builder.load_string(screen_kv), title)
def add_tab(self, screen, title):
add_tab(self.screen.ids.nav_drawer,
self.screen.ids.screen_manager,
self.screen.ids.menu_list,
screen,
title)
if __name__ == "__main__":
app = App()
app.run()
PROBLEM: If I want to add new screens after the app was build I can't access app.screen element !!!
The app.add_tab() method doesn't work anymore !!
Why can't I save the reference of the app's screen ? How should I do to keep this reference ?
Here is an example of what I want to do but it fail (same code, I just changed the end, trying to add_tab after the creation).
if __name__ == "__main__":
app = App()
app.add_tab(Builder.load_string(screen_kv), "Contact Us")
app.run()
ERROR MESSAGE: "AttributeError: 'App' object has no attribute 'screen'"
The error message is pretty clear - your App has no attribute screen because you haven't set that attribute yet, which in turn is because you haven't run your build method yet. Don't try to modify the screen before it exists.

Kivy, ScreenManager says no screen while switching to previous screen

Now I'm already using ScreenManager in a different project where it works with no issues. But amazingly same setup doesn't work in any other projects. Simply can't figure out why my screen can only be switched to a new screen (which was not displayed before) and not to any previous screens. I'm posting a simple code to show this phenomenon. Please advice what am I seriously missing. Thank you.
screenmanager_test.py
import kivy
kivy.require('1.11.0')
import os
os.environ['KIVY_GL_BACKEND'] = 'gl'
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition
from kivy.clock import Clock
class MainScreen(Screen):
def setName(self,*args):
FirstPopup().open()
class SecondScreen(Screen):
pass
class FirstPopup(Popup):
pass
class MyScreenManager(ScreenManager):
pass
class SmTestApp(App):
def build(self):
sm = MyScreenManager()
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(MainScreen(name="main_scr"))
sm.add_widget(SecondScreen(name="second_scr"))
return sm
SmTestApp().run()
smtest.kv
#: kivy 1.11.0
<MainScreen>:
name: 'main_scr'
BoxLayout:
orientation: 'vertical'
padding: 100,100
spacing: 50
Label:
text: 'Welcome to Main Screen'
font_size: 35
Button:
text: 'Options'
font_size: 15
on_release: root.setName(*args)
Button:
text: 'Next Screen'
font_size: 15
on_release: app.root.current = 'second_scr'
<SecondScreen>:
name: 'second_scr'
BoxLayout:
orientation:'vertical'
padding:100,100
spacing:50
Label:
text: 'This is your second screen'
font_size: 35
Button:
text: 'Back'
font_size: 25
on_release: app.root.current = 'main_scr'
<FirstPopup>:
title: 'Options Window'
size_hint: None, None
size: 400,370
BoxLayout:
orientation : 'vertical'
Label:
text : "Checkbox options listed here"
Button:
text: "OK"
on_release: root.dismiss()
Ok. Here is what I have found. I removed that FadeTransition effect and things came back to normal. I checked all other available transition methods and found that out of 8 , 4 transition style causes the program to exit. The effects which are causing this error are Fade, Wipe, FallOut and RiseIn.
If this is caused due to some other wrongly set paramenter, I don't know. If anyone can give an explanation then that would be fine. If not any how the cause has been identified. Thank you #John Anderson.

flexible number of objects in kivy scrollview

I am trying to use input on one screen to make a list on a second screen using kivy. The basic idea is that a paragraph is broken up into words, which are then displayed on another page. Below is a minimum example.
What is happening is that the words are getting squished together, and there's no scrolling.
In main.py:
from kivy.app import App
from kivy.app import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
class InputScreen(Screen):
wordList = ObjectProperty()
def getwords(self):
self.wordList=[]
s = self.ids.textInput.text.split()
for w in s:
for punctuation in [',','.','!','?']:
w.strip(punctuation)
self.wordList.append(w)
return None
class WordLabel(Label):
pass
class WordScreen(Screen):
wordList = ObjectProperty()
def updateWords(self):
self.ids.scrollContainer.clear_widgets()
wordGrid = GridLayout(cols=2,size_hint_y=None)
wordGrid.bind(minimum_height=wordGrid.setter('height'))
for w in self.wordList:
wordGrid.add_widget(Label(text=w))
wordGrid.add_widget(Label(text='property of word'))
self.ids.scrollContainer.add_widget(wordGrid)
class testScreenManager(ScreenManager):
pass
class testApp(App):
def build(self):
sm = testScreenManager()
return sm
if __name__=='__main__':
testApp().run()
In test.kv:
<testScreenManager>
InputScreen:
id: inputScreen
name: 'input'
WordScreen:
id: wordScreen
name: 'word'
wordList: inputScreen.wordList
<InputScreen>:
GridLayout:
cols: 1
TextInput:
id: textInput
hint_text: 'Input paragraph'
BoxLayout:
orientation: 'horizontal'
size_hint_y: .2
Button:
text: 'Analyze paragraph'
on_press: root.getwords()
Button:
text: 'See word list'
on_press:
root.getwords()
root.manager.transition.direction = 'left'
root.manager.current = 'word'
BoxLayout:
orientation: 'horizontal'
size_hint_y: .2
Label:
id: wordCountResult
text: ''
<WordScreen>:
on_enter: root.updateWords()
BoxLayout:
orientation: 'vertical'
BoxLayout:
id: rowLabels
orientation: 'horizontal'
size_hint_y: .2
Label:
text: 'Words not at grade level:'
size_hint_x: .5
Label:
text: 'Grade Level'
size_hint_x: .5
ScrollView:
id: scrollContainer
size_hint: (None,None)
size: (self.parent.width,self.parent.height - rowLabels.height - buttonRow.height)
Button:
id: buttonRow
size_hint_y: .2
text: 'Back'
on_press:
root.manager.transition.direction = 'right'
root.manager.current = 'input'
<WordLabel>:
size_hint_y: None
height: '29sp'
I've tried using size_hint_y=.6 for the ScrollView, and swapping height and minimum_height in the wordGrid.bind(minimum_height=wordGrid.setter('height')).
What I thought was going to happen, is that the ScrollView container will be the height of the window minus the height used by two other widgets. Inside the ScrollView, WordGrid is longer (the height is bound to the minimum height necessary for all children) and so the scrolling is used to see all the items. I must not be understanding some fact about heights/sizes of ScrollView, WordGrid, and WordGrid's children. Anyone have some insight?
In this part of the code:
for w in self.wordList:
wordGrid.add_widget(Label(text=w))
wordGrid.add_widget(Label(text='property of word'))
the Label by default will have size_hint_y=1. Instead, size_hint_y=None is necessary. A height can be set, as is done in the WordLabel class - which was what the OP (by me) meant to do.
Thanks to related question Kivy ScrollView - Not Scrolling for helping me to realize the error.

Categories

Resources