Kivy function scope on button release (widget tree) - python

I am new to Kivy and need some help in understanding function scope. I have built a simple app with two screens. The first screen has two buttons and the second screen has a text label at the centre. On the first screen, I have used app.root.current='new_screen_name' with the on_release attribute for one button and it works fine. It takes me to the next screen which is the intended function. For the second button, I have used a function call which has been defined in the Python file under the class definition of the first screen (the root widget of the button). However, this method does not work and the app window simply closes. I guess I am making a mistake in the function scope and call But I cannot figure out what. Any help would be greatly appreciated.
Python file:
from kivy.config import Config
# Config.set should be used before importing any other Kivy module.
Config.set('kivy','window_icon','sivaicon.png')
# Config set for resizing image button
Config.set('graphics', 'resizable', True)
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.lang.builder import Builder
class SivaLoginScreen(Screen):
def func_authentication(self):
app.root.current='tabbed_screen'
class SivaTabbedScreen(Screen):
pass
class SivaScreenManager(ScreenManager):
pass
class ImageButton(ButtonBehavior, Image):
pass
# Tell Kivy to directly load a file. If this file defines a root widget, it will be returned by the method.
root_widget = Builder.load_file('siva.kv')
class SivaApp(App):
def build(self):
# Initialize root widget
return root_widget
if __name__ == '__main__':
# Run application
SivaApp().run()
Kivy file (.kv):
SivaScreenManager:
SivaLoginScreen:
SivaTabbedScreen:
<ImageButton>:
keep_ratio: True
<SivaLoginScreen>:
name: 'login_screen'
canvas.before:
Color:
rgba: 195/255, 60/255, 35/255, 1
Rectangle:
pos: self.pos
size: self.size
FloatLayout:
size: root.width, root.height
Image:
id: login_logo_siva
source: 'images/sivalogo4.png'
keep_ratio: True
size_hint: 0.2, 0.2
pos_hint: {'center_x':0.5, 'center_y':0.75}
Label:
id: login_label_siva
pos: self.x*0.5-4, self.y*0.5+15
markup: True
font_name: 'roboto/Roboto-Medium.ttf'
text: '[color=#FDFD98]S.[/color][color=#B29DD9]I[/color][color=#FDFD98].[/color][color=#77DD77]V[/color][color=#FDFD98].[/color][color=#779ECB]A[/color]'
font_size: '50sp'
Label:
id: login_label_slogan1
pos: self.x*0.5-3, self.y*0.5-6
markup: True
font_name: 'roboto/Roboto-Regular.ttf'
text: '[color=#FDFD98]SLOGAN TEXT[/color]'
font_size: '15sp'
Label:
id: login_label_slogan2
pos: self.x*0.5-3, self.y*0.5-20
markup: True
font_name: 'roboto/Roboto-Regular.ttf'
text: '[color=#FDFD98]HEADLINE TEXT[/color]'
font_size: '15sp'
BoxLayout:
id:login_button_layout
orientation: 'horizontal'
size_hint: 0.2, 0.2
pos_hint: {'center_x':0.5, 'center_y':0.25}
ImageButton:
id: first_button
source: {'normal': 'images/first.png', 'down': 'images/first-down.png'} [self.state]
on_release: app.root.current='tabbed_screen'
ImageButton:
id: second_button
source: {'normal': 'images/second.png', 'down': 'images/second-down.png'} [self.state]
on_release: app.root.func_authentication()
<SivaTabbedScreen>:
name: 'tabbed_screen'
FloatLayout:
size: root.width, root.height
Label:
pos: self.x*0.5, self.y*0.5
text: 'SECOND SCREEN'
font_size: '50sp'

In your case, app.root link to SivaScreenManager which is the root widget of your application. And in these class, there is not a func_authenticationfunction, why you app crashed.
To refer a class itself in a KV definition, you must just use root, so right code must be :
on_release: root.func_authentication()
see Kivy Language - Reserved Keywords
Definition of func_authentication is not correct also, app is unknown. Use either :
App.get_running_app().root.current='tabbed_screen' or
self.manager.current='tabbed_screen'

Related

AttributeError: 'super' object has no attribute '__getattr__' in Kivy

I am facing an error when trying to add buttons to a specific GridLayout using ids. Ideally the code below should generate 10 buttons in the GridLayout with an id of grids, but instead the error that shows up is
AttributeError: 'super' object has no attribute 'getattr'
The code in my main.py file is -->
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.scrollview import ScrollView
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.clock import mainthread
Builder.load_file("design.kv")
class RoundedButton(Button):
pass
class RootWidget(ScreenManager):
pass
class MainScreen(Screen):
def on_enter(self):
for i in range(10):
button = RoundedButton()
self.ids.grids.add_widget(button)
class MainApp(App):
def build(self):
return RootWidget()
class RootWidget(ScreenManager):
pass
if __name__ == "__main__":
MainApp().run()
and the code in my design.kv file is -->
<MainScreen>:
canvas.before:
Color:
rgba: (245/255,245/255,245/255,245/255)
Rectangle:
pos: self.pos
size: self.size
GridLayout:
id: box1
cols:1
spacing:5
GridLayout:
id: box
cols:1
size_hint_y: 0.10
TextInput:
id: ti
hint_text: 'Search'
size_hint: 1, 0.05
text_size: self.width, self.height
background_normal: ''
ScrollView:
id: scrolls
do_scroll_x:False
spacing: 10, 5
GridLayout:
id: grids
cols:1
RoundedButton:
text: "hi"
size_hint: .98, .25
<RoundedButton#Button>
background_color: (0,0,0,0)
background_normal: ''
canvas.before:
Color:
rgba: (1,0,0,1)
RoundedRectangle:
size: self.size
pos: self.pos
radius: [10]
<RootWidget>:
MainScreen:
name: "main_screen"
I was wondering what the issue is
You need to call another function with Clock.schedule_once in on_enter function and try to load your widgets in it. It happens because kivy doesn't let you add widgets before first frame so we can give order kivy to add widgets soon as possible with that.
Example code below:
def on_enter(self):
Clock.schedule_once(self.load_buttons)
def load_buttons(self,*args):
for i in range(10):
button = RoundedButton()
self.ids.grids.add_widget(button)

BoxLayout 'cols' doens't work in AnchorLayout

Here is my kivyfile.kv
WindowManager:
FirstWindow:
SecondWindow:
<FirstWindow>:
name: "first"
AnchorLayout:
anchor_x: "left"
anchor_y: "top"
BoxLayout:
cols:2
orientation: 'vertical'
size_hint: None, None
size: self.minimum_size
Button:
text: "Button 1"
size_hint: (None,None)
width:200
height:30
Button:
text: "Button2"
size_hint: (None,None)
width:200
height:30
TextInput:
id: url
multiline: False
size_hint: (None,None)
width:200
height:30
TextInput:
id: url2
multiline: False
size_hint: (None,None)
width:200
height:30
Here is my .py file:
import kivy
kivy.require('1.11.1')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class FirstWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
kv=Builder.load_file('kivyfile.kv')
class Downloader(App):
def build(self):
return kv
if __name__ == '__main__':
Downloader().run()
What I am trying to achive, is to get a BoxLayout at the top left corner, which has two columns - Button and TextInput NEXT TO it. However, it only does all components one under each other. I have also a future question on how to add another BoxLayout NEXT TO the previous BoxLayout that I have. As in - how to make it be on the right side of existing BoxLayout, how to position it.

Python Kivy self.add_widget() doesn't update while code running

The idea is to create a texting app that works like Messenger. I am having a problem with the chat history which is a "BoxLayer (or GridLayer)" containing all previous text. I want when I insert a new text, it's will appear as a new label or a box and stay below the previous text like this, but when I run the code and insert input text, it's not appearing. I spent hours to find the answer both myself and on the internet, but it's kind of hard for a beginner like me.
.Py file
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.core.image import Image
from kivy.properties import StringProperty
from kivy.core.window import Window
from kivy.graphics.context_instructions import Color
class MainWidget(Widget):
request = StringProperty("This is a previous text, don't mind")
insert_text = StringProperty("Insert Here")
window_size = (305,400)
refresh_key = False
def __init__(self,**kwargs):
super().__init__(**kwargs)
self.Window_Minimum()
def on_size(self,*args):
print(self.width,self.height)
def on_text_validate(self,widget): #<<<<<<<<<<<<<<<<<<<<<<<<<<< input text
request=widget.text
Chat_history_update().chat_history(request)
def Window_Minimum(self):
Window.minimum_width,Window.minimum_height=self.window_size
class Chat_history_update(BoxLayout):
def __init__(self,**kwargs):
super().__init__(**kwargs)
l = Label(text="This is a previous text, don't mind",size_hint=(1, None),height=("30dp"))
self.add_widget(l)
def chat_history(self,request): # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Add Label Function
l = Label(text=request, size_hint=(1, None),height=("30dp"))
self.add_widget(l) # <<<<<<<<<<<<< This won't update my app screen
class Assistant(App):
pass
if __name__ == "__main__":
Assistant().run()
Kv file
MainWidget:
<MainWidget>:
BoxLayout:
size: root.size
orientation: "vertical"
GridLayout:
cols: 3
size_hint: 1,None
height: "50dp"
spacing: "10dp"
padding: "10dp"
Label:
text:"Erza Assistant"
Button:
text:"Edit Path"
Button:
text:"Setting"
GridLayout:
size: self.size
rows: 2
spacing: "10dp"
padding: "10dp"
ScrollView: #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Here my text display
Chat_history_update:
orientation: "vertical"
size_hint: 1, None
height: self.minimum_height
TextInput:
size_hint: 1, None
height: "40dp"
text: root.insert_text
multiline: False
on_text_validate: root.on_text_validate(self)
Your code:
Chat_history_update().chat_history(request)
is creating a new instance of Chat_history_update, and calling chat_history() for that new instance. That new instance is not part of your GUI, so you will see no effect. The fix is to access the correct instance of Chat_history_update (the one that is in your GUI). To do that, you can add an id in your kv:
ScrollView: #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Here my text display
Chat_history_update:
id: chu
orientation: "vertical"
size_hint: 1, None
height: self.minimum_height
And then use that id in your py code:
def on_text_validate(self,widget): #<<<<<<<<<<<<<<<<<<<<<<<<<<< input text
request=widget.text
self.ids.chu.chat_history(request)
I think this here might help. You have to reference widgets in the kivy language by using id or ids.
If you do not yet i strongly suggest you learn how to reference widgets by their ids.

Kivy Cover Whole Screen

This is my kivy code:
Manager:
StartMenu:
SetupMenu:
<StartMenu>
name: "start"
size: root.width, root.height
GridLayout:
Image:
source: "Logo.png"
keep_ratio: True
size_hint: 1,1
AnchorLayout:
anchor_x: "center"
anchor_y: "bottom"
Button:
background_normal: ''
background_color: 242, 242, 242, .50
width: root.width/5
text: "Begin"
size_hint: None, None
on_release:
app.root.current: "setUp"
root.manager.transition.direction: "left"
</StartMenu>
<SetupMenu>
name: "setUp"
Button:
text: "Go Back"
on_release:
app.root.current: "startMenu"
root.manager.transition.direction: "left"
This is my Python code:
import kivy
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.relativelayout import RelativeLayout
=from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.core.window import Window
class StartMenu(Screen):
pass
class SetupMenu(Screen):
pass
class Manager(ScreenManager):
pass
class StartMenu(Screen):
pass
kv = Builder.load_file("lordofthe90.kv")
class lordOfThe90(App):
def build(self):
return kv
lordOfThe90.run()
How do I make both screens cover the entire window? I force a full screen within my game so I'd require their size to be the same as the screen resolution of the device they're on.
Also, does my code roughly look good? I'm very new to kivy as a whole.
I had to define at least rows: 1 in GridLayout: to display it correctly.

Extremely oversized/large buttons inside Kivy Screen class

For the sake of brevity I'll explain my problem before posing the code. Here it goes:
I have a Screen class in Kivy that holds two widgets, a GridLayout and an Image. The latter is fine, but the buttons are extremely oversized:
And here's my main.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.factory import Factory
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
from kivy.config import Config
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
import configparser
Builder.load_file('kv\MainMain.kv')
#read configurations
config = configparser.RawConfigParser()
config.read('config.ini')
#read config values
width = config.getint('Default', 'MAX_WINDOW_WIDTH')
height = config.getint('Default', 'MAX_WINDOW_HEIGHT')
border = config.getint('Default', 'BORDERLESS')
#apply config values
Config.set('graphics','width', width)
Config.set('graphics', 'height', height)
Config.set('graphics', 'borderless', border)
Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
#create screen manager
class ScreenManager(ScreenManager):
pass
#create background widget
class BackGround(Image):
pass
#image buttons
class MainButtons(GridLayout):
pass
#create main screen:
class MainScreen(Screen):
pass
#create main app
class MainMainApp(App):
def build(self):
return MainScreen()
#register class
Factory.register(MainScreen, cls=MainScreen)
#run
if __name__ == '__main__':
MainMainApp().run()
And here's my kv file:
<ScreenManager>:
id: screen_manager
MainScreen:
id: main_screen
name: 'MainScreen'
manager: screen_manager
ReadScreen:
id: read_screen
name: 'ReadScreen'
manager: screen_manager
<MainScreen>:
BackGround:
id: back_ground
source: 'images\\app_background.jpg'
size: root.width, root.height
MainButtons:
cols: 1
pos: root.width / 2 - 100, root.height / 4
size: 20, 10
Button:
id: button_read
text: "Read"
on_press: root.callback_read()
Button:
id: button_add
text: "Add"
Button:
id: button_manage
text: "Manage"
I'm really swamped on this one. Thanks for your help.
You can use size_hint for this.
Here is an example
The pyhton file:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen,ScreenManager
Builder.load_file("my.kv")
class MainButtons(GridLayout):
pass
class MainScreen(Screen):
pass
class MainMainApp(App):
def build(self):
self.sm = ScreenManager()
self.sm.add_widget(MainScreen(name="main"))
return self.sm
if __name__ == '__main__':
MainMainApp().run()
and kivy file:
<MainScreen>:
MainButtons:
cols: 1
rows: 4
Label:
font_size: "40sp"
text: "Something"
Button:
size_hint: (1,0.1)
text: "Read"
Button:
size_hint: (1,0.1)
text: "Add"
Button:
size_hint: (1,0.1)
text: "Manage"
And output will be:
And if you want the buttons to be smaller in width, you add ´size_hint_x: None´ to your kv file, like this.
<MainScreen>:
MainButtons:
cols: 1
rows: 4
Label:
font_size: "40sp"
text: "Something"
Button:
size_hint: (1,0.1)
size_hint_x:None
text: "Read"
Button:
size_hint: (1,0.1)
size_hint_x:None
text: "Add"
Button:
size_hint: (1,0.1)
size_hint_x:None
text: "Manage"
Output will now be:

Categories