How do I dynamically add widget to grid? - layout

I have a Button in the top boxlayout that I want to add a widget to my grid layout below.
Here is my .py file code
class rootWidget(BoxLayout):
pass
class topWidget(BoxLayout):
print("top part")
class middelWidget(GridLayout):
pass
class bottomWidget(BoxLayout):
pass
class NewButton(Button):
pass
class mainkv(App):
def newButt(self):
print("button clicked")
butt = NewButton(text="New Button")
middelWidget().add_widget(butt)
def build(self):
return rootWidget()
mainkv().run()
And here is my .kv file
<topWidget>:
Button:
text: 'CLick add to grid'
on_press:
app.newButt()
<middelWidget>:
cols: 6
id: grid
Button:
text: 'middel Grid'
<bottomWidget>:
Button:
text: 'bottom'
<rootWidget>:
orientation: 'vertical'
Button:
text: 'Root'
topWidget:
middelWidget:
bottomWidget:
I am not getting any errors but it's not working.
Can't figure out what am missing any help much appreciated.

The problem is that the code:
middelWidget().add_widget(butt)
is creating a new instance of the middelWidget, and adding the Button to that new instance. However, that new instance of middelWidget is not part of your GUI, so nothing happens. The fix is to use the instance of middelWidget that is already in your GUI. That can be done using ids. Here is a modified version of your rootWidget rule in the kv:
<rootWidget>:
orientation: 'vertical'
Button:
text: 'Root'
topWidget:
middelWidget:
id: middle # id for accessing middelWidget
bottomWidget:
Then the newButt() method can be:
def newButt(self):
print("button clicked")
butt = NewButton(text="New Button")
middle = self.root.ids.middle # get a reference to the middelWidget instance
middle.add_widget(butt)
# middelWidget().add_widget(butt)
Also, your classes should begin with and upper case letters. This is not just for appearance, it can cause errors when those classes are used in kv.

Related

How to add widgets to a rule both from kv file and py file in the same time?

Is there a way so that I can add widgets to a specific rule in kivy both from kivy file and python file at the same time?
.kv
<A>:
GridLayout:
cols:1
id: someID
Button:
text:"some text"
on_press: root.do_some_stuff()
<RootWidget>:
A:
name: "rule"
.py
class A(Screen):
def do_some_stuff(self):
print("I was told to do some stuff")
class RootWidget(ScreenManager):
pass
class MainApp(App):
def build(self):
return RootWidget()
How can I add another label to GridLayout widget from the class A?
You can use the id that you have specified in the kv:
class A(Screen):
def do_some_stuff(self):
self.ids.someID.add_widget(Label(text="I was told to do some stuff"))

python kivy, why my "enter" button with "on_keyboard_down" not working

I am working on the same issue from my previous post. Now I added screen manager, and I can switch to different pages.
why is my kivy program not calling the function from another class?
I am still confused about OOP with GUIs, however I tried the following, and none of them worked.
I tried to add an instance of Screen 1() in the main app(), and it did nothing. Then, I tried to add the contractor method init into Screen_1() and it says I don't have a build method. And few other ideas that didn't work at all.
Then I realized that there is no link between Screen_Manager() and Screen_1() because all of my methods are in Screen_1(), but the build method is returning Screen_Manager(). On my kv file, there is this code:
<Screen_Manager>:
Screen_1:
Screen_2:
isn't this is where the program "links" between the Screen_Manager() class to other classes?
if someone can help me understand what I am not understanding and help me correct my problem, it would help me to learn about kivy. I understand, <> is like apply to rules, which has 2 widgets screen 1 and screen 2, and also have their own rules.
here is my main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.lang.builder import Builder
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.modules import keybinding
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
class Screen_Manager(ScreenManager):
pass
class Screen_1(Screen):
main_display = ObjectProperty()
text_input = ObjectProperty()
def plus_1(self):
self.value = int(self.main_display.text)
self.main_display.text = str(self.value + 1)
def minus_1(self):
self.value = int(self.main_display.text)
self.main_display.text = str(self.value - 1)
def up(self):
self.main_display.text = self.text_input.text
self.text_input.text = ''
class Keyboard(Widget):
def __init__(self, instance):
super().__init__()
self.a = instance
self.keyboard = Window.request_keyboard(None, self)
self.keyboard.bind(on_key_down=self.on_keyboard_down)
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'enter':
self.a.up()
return True
class Screen_2(Screen):
pass
class MainApp(App):
def build(self):
self.title = "Number Control App"
self.sm = Screen_Manager()
key = Keyboard(self.sm)
return self.sm
if __name__=="__main__":
MainApp().run()
my kv file
<Screen_Manager>:
Screen_1:
Screen_2:
<Screen_1>:
name: "first"
main_display: display_1
text_input: text_input_1
#------inherenting from BoxLayout
BoxLayout:
orientation: "vertical"
#------begining of conent--
BoxLayout:
size_hint_y: 1
#-nexted content------
Label:
id: display_1
text: "0"
font_size: "150sp"
background_color: 0.1, 0.5, 0.6,
#--------
BoxLayout:
size_hint_y: .35
orientation: "horizontal"
#-nexted content------
Button:
text: "-"
font_size : "60sp"
on_press: root.minus_1()
#-nexted content------
Button:
text: "+"
font_size : "35sp"
background_color: (0, 1, 0, 1)
on_press: root.plus_1()
#--------
BoxLayout:
size_hint_y: .15
orientation: "horizontal"
#-nexted content-------
Button:
text: "Config"
size_hint_x: .25
on_release:
root.manager.current = "second"
root.manager.transition.direction = "left"
#-nexted content-------
TextInput:
id: text_input_1
size_hint_x: 1
hint_text: "Enter your initial # here"
multiline: False
#-nexted content-------
Button:
text: "Up"
size_hint_x: .25
on_press: root.up()
<Screen_2>:
name: "second"
Button:
text: "Go Back"
on_press:
app.root.current = "first"
root.manager.transition.direction = "right"enter code here
thanks again for taking the time to help.
Not sure I understand your question, but pressing Enter in your TextInput executed your code:
self.a.up()
but a in your KeyBoard is the Screen_Manager as set at the line:
self.sm = Screen_Manager()
key = Keyboard(self.sm)
and Screen_Manager has no up method. You can fix that by changing the code in your KeyBoard code to:
self.a.current_screen.up()
Note that this will only work when the current screen is Screen_1.

Python/Kivy - Sending variables from one class to another

I'm not that new to Python so I have a basic understanding but I wouldn't say that I'm all that great either.
I've been having a tonne of problems with a brazilian jiujitsu app that I'm trying to make in Kivy. I am trying to get the text from a button that has been pressed in one window, and then use that text in a label for the next window that shows. The former window I have given the class name GuardWindow() and the latter window is called SweepSubTranPage().
I have managed to send the name of the button from my kivy file to the class GuardsWindow() easily enough, and the line self.guardlabel = instance.text works fine and retrieves the button name perfectly. My main problem however is sending that name over to the SweepSubTranPage() so that i can access it in my kivy file.
Here is the Python code:
class GuardsWindow(Screen):
guardButtonName = ObjectProperty(None)
def send_guard_type(self, instance, **kwargs):
self.guardButtonName = instance.text # retrieves the button name
def sender(self): # my attempt at sending it to the other class
return self.guardButtonName
class SweepSubTranPage(Screen):
pageTitle = ObjectProperty(None)
gw = GuardsWindow()
pageTitle.text = gw.sender()
kv = Builder.load_file("my.kv")
class MyMainApp(App):
def build(self):
return kv
if __name__ == "__main__":
MyMainApp().run()
And here is the Kivy file code:
<GuardsWindow>
name: "GuardsHomepage"
ScrollView:
bar_width: 10
do_scroll_x: False
do_scroll_y: True
scroll_type: ["bars", "content"]
BoxLayout:
orientation: "vertical"
size_hint: 1, 2
padding: 10
spacing: 10
Button: # these are the buttons that i am getting the text from
text: "Full Guard"
on_press:
root.send_guard_type(self) # this sends the button
on_release:
app.root.current = "SweepSubTranPage"
root.manager.transition.direction = "left"
Button:
text: "Half Guard"
on_press:
root.send_guard_type(self) # this sends the button
on_release:
app.root.current = "SweepSubTranPage"
root.manager.transition.direction = "left"
<SweepSubTranPage>
name: "SweepSubTranPage"
pageTitle: title
BoxLayout:
orientation: "horizontal"
size_hint: 1, 0.1
Label: # this is the label that I need the text for
id: title
font_size: 40
size_hint: 1, 1
When I run the code above, I get the error:
File "C:/Users/bensw/PycharmProjects/BJJ Tracker/main.py", line 36, in <module>
class SweepSubTranPage(Screen):
File "C:/Users/bensw/PycharmProjects/BJJ Tracker/main.py", line 40, in SweepSubTranPage
pageTitle.text = gw.sender()
AttributeError: 'kivy.properties.ObjectProperty' object has no attribute 'text'
I hope I have been clear enough in explaining my issue. If you have any questions please ask!
Thank you so much!!
In your send_guard_type() method you can access another Screen using the manager property of any Screen, and the get_screen() method of the ScreenManager. Something like this:
def send_guard_type(self, instance, **kwargs):
self.guardButtonName = instance.text # retrieves the button name
self.manager.get_screen("SweepSubTranPage").ids.title.text = instance.text

Add custom widgets at runtime to screens

I want to add Buttons (Basicly custom Buttons with Image) as a custom Widgets to "Screen1" but I always end up with "_event.pyx not found" Error.
I've tried with "super().init(**kwargs)" and without.
Python code:
sm = ScreenManager()
class DrinkWidget(Widget):
pass
class HomeScreen(BoxLayout):
def switch(self, to):
#Swithing funktion
#This is the Part, that causes the Problem I think:
class Screen1(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add_widget(DrinkWidget(
lable_text_optn = 'test'
))
class Screen2(Screen):
pass
class ZapfanlageApp(App):
icon = 'GUI_Elemente/app_icon.png'
title = 'Zapfanlage'
def build(self):
pass
if __name__ == "__main__":
ZapfanlageApp().run()
Kivy code (separate .kv File. The part "HomeScreen" works so far):
HomeScreen:
sm: sm
name: 'ScreenManager'
BoxLayout:
orientation: 'vertical'
rows: 2
ActionBar:
pos_hint: {'top': 1}
size_hint_y: .065
ActionView:
ActionButton:
text: 'Cocktails'
on_press:
root.switch(1)
ActionButton:
text: 'Drinks'
on_press:
root.switch(2)
ActionButton:
text: 'Einstellungen'
on_press:
root.switch(3)
ScreenManager:
id: sm
size_hint_y: .935
Screen1:
name: "screen1"
id: screen1
Screen2:
name: "screen2"
id: screen2
<Screen1#Screen>:
name: "screen_1"
id: screen1
#Here should the Buttons in GridLayout appear
<Screen2#Screen>:
name: "screen_2"
id: screen2
#This is the Custom Button I want to be inserted above
<Drink_Widget#Button>:
image_path_optn: image_path
lable_text_optn: lable_text
Button:
size_hint_x: None
size_hint_y: None
height: (root.height) -10
width: 250
on_press:
BoxLayout:
orientation: "vertical"
width: root.width
height: root.height
pos_hint: root.pos
pos: root.pos
padding: 5
Image:
source: image_path
Label:
text: label_text
I want to show a various number of DrinkWidgets on screen1 vertically and add them in runtime. But I always end up with nothing showing up or with _event.pyx not found error. Passing the code under <Screen1#Screen>: directly works.
I hope someone can help me. Thanks a lot!
Okay, it looks like you want to add a number of your DrinkWidgets to your screen when your app loads. First things first, in your .py file you have defined a class named Drink_widget but in .kv you call it DrinkWidget
Next, since you have your DrinkWidget defined as inheriting the Button class from kivy, you can easily change the text in the DrinkWidget using the text: field. Similarly, you can change the image that the button displays to be whatever you like using the background_normal: field. To change the image displayed when you click the button, use the background_down: field. Example:
<DrinkWidget#Button>:
text: "some text"
background_normal: "image1.png"
background_down: "image2.png"
So you don't need your lable_text_optn or image_path_optn fields.
Also, you are trying to add a number of widgets to a Screen widget, when really you should be adding a number of widgets to a Layout widget (FloatLayout, BoxLayout, or GridLayout). Your Screen widget should only have the Layout widget as its direct child.
Another issue I see is you have two root widgets inside your .kv file -- HomeScreen and BoxLayout unless your indentation is correct in the question.
Here is a minimal example of what I believe you are trying to get working:
main.py
from kivy.app import App
from kivy.uix.button import Button
class DrinkWidget(Button):
pass
class MainApp(App):
def on_start(self):
# This command is automatically called when your app loads up
the_screen_grid = self.root.ids.some_descriptive_id
# self.root refers to the root widget, which is the GridLayout
# self.root.ids gets me a DictProperty of all children widgets that have an id associated with them
# self.root.ids.some_descriptive_id gets me the GridLayout widget I defined with the id: some_descriptive_id
for i in range(3):
the_screen_grid.add_widget(DrinkWidget(text="drink " + str(i)))
MainApp().run()
main.kv
GridLayout:
cols: 1
Screen:
# Widgets that aren't Layouts normally only have 1 child widget
# To "add multiple widgets" to a screen, give the screen a Layout, then add widgets to the layout
GridLayout:
id: some_descriptive_id
rows: 1
Your code is a bit too long to give you an exact solution for your case, but I hope this example gives you the knowledge to fix it up for yourself!

How to get/pass current ScreenManager to button callback function? (no kv file in use)

Question is simple. I have ScreenManager as sm variable, how do I get that, and screen information in to button callback. Thank you already for possible solutions.
Below is the issue part of my code, I have commented the section that I need to solve:
def build(self):
def callback_home(dt):
# How do I get the current sreenmanager here so
# things would work as below
sm.switch_to(screen1, direction='left')
app = FloatLayout()
# Ui top
anchor_top = AnchorLayout(anchor_x='center', anchor_y='top')
app.add_widget(anchor_top)
box_top = BoxLayout(orientation='horizontal',
size_hint=(1.0, 0.1))
anchor_top.add_widget(box_top)
btn_home = Button(text='Home')
btn_home.bind(on_press=callback_home) #Callback line is in here
btn_add = Button(text='Add')
btn_upload = Button(text='Upload')
box_top.add_widget(btn_home)
box_top.add_widget(btn_add)
box_top.add_widget(btn_upload)
# Ui bottom
anchor_bottom = AnchorLayout(anchor_x='center', anchor_y='bottom')
app.add_widget(anchor_bottom)
sm = ScreenManager(size_hint=(1.0, 0.9))
anchor_bottom.add_widget(sm)
screen1 = Screen(name='Home')
screen1.add_widget(Label(text='Home'))
sm.add_widget(screen1)
screen2 = Screen(name='Add')
screen2.add_widget(Label(text='Add'))
sm.add_widget(screen2)
screen3 = Screen(name='Upload')
screen3.add_widget(Label(text='Upload'))
sm.add_widget(screen3)
return app
Define your callback function after your create the ScreenManager. But this is 10 times easier if you use kv language.
Also - ScreenManager.switch_to() only works with new Screens. Using this method to switch to a Screen which has already been added to a ScreenManager will throw an Exception.
Finally, your layout can be much simpler if you use a BoxLayout instead of a FloatLayout and AnchorLayouts.
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: 0.1
Button:
text: 'Home'
on_press: sm.current = 'Home'
Button:
text: 'Add'
on_press: sm.current = 'Add'
ScreenManager:
id: sm
Screen:
name: 'Home'
Label:
text: 'Home'
Screen:
name: 'Add'
Label:
text: 'Add'

Resources