I want to display a single image over multiple different buttons (as shown in the picture: https://i.stack.imgur.com/BQ99R.jpg)
When I try to do so, the image gets covered by the buttons and hidden in the background.
What I want to know is that is this even possible in kivy? If yes, where do I define the image, in the label or button?
This code:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
Builder.load_string("""
<Base>:
GridLayout:
cols: 2
size_hint: 1, 1
Button:
text: "Button 1"
on_press: print(self.text)
Button:
text: "Button 2"
on_press: print(self.text)
Button:
text: "Button 3"
on_press: print(self.text)
Button:
text: "Button 4"
on_press: print(self.text)
Image:
source: 'wolf.png'
""")
class Base(FloatLayout):
def __init__(self, **kwargs):
super(Base, self).__init__(**kwargs)
class SampleApp(App):
def build(self):
return Base()
if __name__ == "__main__":
SampleApp().run()
produces this:
Of course you have to provide an image yourself ;o)
Related
I have this code, there are two Buttons, "Normal Button" and "Remove Button" in a Screen. I need the Normal Button to be removed on pressing the "Remove Button"... Please help, need it for a imp project.
Main code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen,ScreenManager
class Main(Screen):
def remove(self):
self.remove_widget(self.btn) # I'm not sure what to put here
class Manager(ScreenManager):
pass
kv=Builder.load_file("btn.kv")
screen=Manager()
screen.add_widget(Main(name="main"))
class Test(App):
def build(self):
return screen
Test().run()
KV code:
<Main>:
name: "main"
GridLayout:
id: GL
cols: 1
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.5}
Button:
id: btn
text: "Remove Button"
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.6}
Button:
id: btn2
text: "Normal Button"
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.4}
on_press:
root.remove()
First, if you want the Remove Button to do the remove, then you must bind the Remove Button to the remove() method:
Button:
id: btn
text: "Remove Button"
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.6}
on_press:
root.remove(btn2)
Button:
id: btn2
text: "Normal Button"
size_hint: (.5,.5)
pos_hint: {"center_x":.5,"center_y":.4}
You can pass the Button to be removed to the remove() method by using the btn2 id as an argument to the remove() method.
Then you can use that argument in the remove() method:
class Main(Screen):
def remove(self, butt):
gl = self.ids.GL
if butt in gl.children:
gl.remove_widget(butt)
else:
print('already removed')
Below is a working snippet example of a program that presents the user with menu popups to enter info.
The issues is getting the dismiss bindings working correctly. The program flow is currently:
declare content with a return callback
Load content into a popup object
call the popup
__init__ is called and sets up something like _keyboard bindings
User enters data and presses accept
Return call back is called, the popup is no longer needed so we call popup.dismiss()
popup closes and is it
The issue is if I do _keyboard binding in the __init__ then when the popup closes I MUST call the unbind method or else the keyboard input is still calling the old popups functions!
Another thing I dislike is the return callback needing to call self._popup.dismiss(). I think it is much cleaner if the popup is completely self contained and completely reuseable. This is a numpad entry popup, it should bind the keyboard and unbind it by itself. The callback recieves an instance snapshot of the popup so the return data is easy to access. The popup itself should be the one to close itself as it knows for sure that the returnCB() was its final goal.
I have no idea how to implement this though. Binding on_dismiss inside of the __init__ does nothing at all as TouchGoToInput_dismiss is never called. I also cant figure out how to get TouchGoToInput to close itself.
Another issue is if ESC is pressed the popup closes and once again the keyboard binding is messed up.
Can anyone lend me a hand understanding the call case structure?
from kivy.app import App
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty
from kivy.properties import StringProperty
from kivy.core.window import Window
from kivy.uix.popup import Popup
Builder.load_string('''
<TouchGoToInput>:
textInput:textInput
cols: 1
size: root.size
pos: root.pos
GridLayout:
cols: 1
size_hint_y:.25
TextInput:
size_hint_x:1.0
font_size: self.height - 15
padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
id:textInput
disabled: True
GridLayout:
cols: 3
Button:
text: "1"
on_release: root.addText("1")
Button:
text: "2"
on_release: root.addText("2")
Button:
text: "3"
on_release: root.addText("3")
Button:
text: "4"
on_release: root.addText("4")
Button:
text: "5"
on_release: root.addText("5")
Button:
text: "6"
on_release: root.addText("6")
Button:
text: "7"
on_release: root.addText("7")
Button:
text: "8"
on_release: root.addText("8")
Button:
text: "9"
on_release: root.addText("9")
Button:
text: "."
on_release: root.addText(".")
Button:
text: "0"
on_release: root.addText("0")
Button:
text: "Done"
on_release: root.accept()
''')
class TouchGoToInput(GridLayout):
returnCB = ObjectProperty(None)
def __init__(self, **kwargs):
super(TouchGoToInput, self).__init__(**kwargs)
self.bind(on_dismiss=self.dismiss)
print('TouchGoToInput.__init__')
def dismiss(self):
print('TouchGoToInput_dismiss')
def addText(self, text):
self.textInput.text = self.textInput.text + text
def accept(self):
print('TouchGoToInput.accept')
self.returnCB(self)
def __del__(self):
print('TouchGoToInput.__del__')
self.returnCB(self)
class TestApp(App):
def build(self):
self.popupContent = TouchGoToInput(returnCB=self.gotoLinePopup)
self._popup = Popup(title="GoTo...", content=self.popupContent,
size_hint=(0.9, 0.9))
#self._popup.bind(on_dismiss=self.main_dismiss)
return Factory.Button(text="press me", on_press=self._popup.open)
def gotoLinePopup(self, instance):
print('returnCB.text: ', instance.textInput.text)
self._popup.dismiss()
def main_dismiss(self, instance):
print('main_dismiss')
TestApp().run()
In the example, it demonstrates implementation of numpad using Popup widget with keyboard binding. It accepts input from Buttons, Keyboard, and NumPad.
Popup » dismiss
By default, any click outside the popup will dismiss/close it. If you
don’t want that, you can set auto_dismiss to False:
Popup » auto_dismiss
auto_dismiss
This property determines if the view is automatically dismissed when
the user clicks outside it.
auto_dismiss is a BooleanProperty and defaults to True.
Example
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.core.window import Window
Builder.load_string('''
#:kivy 1.11.0
<NumPad>:
title: "GoTo..."
size_hint: (0.9, 0.9)
auto_dismiss: False
<TouchGoToInput>:
textInput: textInput
cols: 1
size: root.size
pos: root.pos
GridLayout:
cols: 1
size_hint_y: .25
TextInput:
size_hint_x:1.0
font_size: self.height - 15
padding_y: [self.height / 2.0 - (self.line_height / 2.0) * len(self._lines), 0]
id: textInput
disabled: True
GridLayout:
cols: 3
Button:
text: "1"
on_release: root.addText(self.text)
Button:
text: "2"
on_release: root.addText(self.text)
Button:
text: "3"
on_release: root.addText(self.text)
Button:
text: "4"
on_release: root.addText(self.text)
Button:
text: "5"
on_release: root.addText(self.text)
Button:
text: "6"
on_release: root.addText(self.text)
Button:
text: "7"
on_release: root.addText(self.text)
Button:
text: "8"
on_release: root.addText(self.text)
Button:
text: "9"
on_release: root.addText(self.text)
Button:
text: "."
on_release: root.addText(self.text)
Button:
text: "0"
on_release: root.addText(self.text)
Button:
text: "Done"
on_release:
app._popup.dismiss()
''')
class TouchGoToInput(GridLayout):
def addText(self, text):
self.textInput.text = self.textInput.text + text
class NumPad(Popup):
def __init__(self, **kwargs):
super(NumPad, self).__init__(**kwargs)
self.popupContent = TouchGoToInput()
self.content = self.popupContent
def on_open(self):
# erase previous textInput
self.popupContent.textInput.text = ''
# keyboard binding
self._keyboard = Window.request_keyboard(
self._keyboard_closed, self, 'text')
if self._keyboard.widget:
# If it exists, this widget is a VKeyboard object which you can use
# to change the keyboard layout.
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
def _keyboard_closed(self):
# keyboard have been closed!
if self._keyboard is not None:
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
# check for 0...9, or '.' pressed from keyboard
if (keycode[0] in list(range(48, 58))) or (keycode[0] == 46):
# keyboard: 0 / 48 to 9 / 57, or decimal / 46
self.popupContent.addText(text)
# check for 0...9, or '.' pressed from numpad
elif (keycode[0] in list(range(256, 267))):
# numpad0 / 256 to numpad9 / 265, or numpaddecimal / 266
if keycode[0] == 266:
self.popupContent.addText('.')
else:
self.popupContent.addText(keycode[1][-1:])
# Keycode is composed of an integer + a string
# If we hit escape, release the keyboard
if keycode[1] == 'escape':
keyboard.release()
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def on_dismiss(self):
print('\tNumPad.on_dismiss: self.popupContent.textInput.text=', self.popupContent.textInput.text)
self._keyboard_closed()
class TestApp(App):
def build(self):
self._popup = NumPad()
return Button(text="press me", on_press=self._popup.open)
if __name__ == "__main__":
TestApp().run()
Output
My app gets data from a database and and is stored into variables in Python. The code below is a simplified version where you have two screens. The first screen has two buttons and the second screen has a label and a back button. The text of the label on the second screen will change depending on what button is pressed.
When run, the label is set to the value of the StringProperty, which is "Test". When one of the buttons are clicked the ChangeScreen function is run and works out the correct new label. The LabelUpdater function on the second is run which should change the string property but doesn't. How do I fix this issue? Thanks <3
Python:
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
class DemoScreen1(Screen):
def ChangeScreen(self, button_text):
if button_text == "Button 1":
new_label = "This is the new label when button 1 is pressed"
DemoScreen2.LabelUpdater(new_label)
else:
new_label2 = "This is the new label when button 2 is pressed"
DemoScreen2.LabelUpdater(new_label2)
self.parent.current = "demoscreen2"
class DemoScreen2(Screen):
screen2_label = StringProperty("Test")
def LabelUpdater(NEW_LABEL):
screen2_label = StringProperty(NEW_LABEL)
class AppScreenManager(ScreenManager):
pass
class Tester(App):
pass
if __name__ == '__main__':
Tester().run()
Kivy:
AppScreenManager:
DemoScreen1:
DemoScreen2:
<DemoScreen1>:
name: "demoscreen1"
orientation: "vertical"
GridLayout:
rows: 2
Button:
id: Button1
text: "Button 1"
on_release: root.ChangeScreen(Button1.text)
Button:
id: Button2
text: "Button 2"
on_release: root.ChangeScreen(Button2.text)
<DemoScreen2>:
name: "demoscreen2"
orientation: "vertical"
GridLayout:
rows:2
Label:
text: root.screen2_label
Button:
text:"Back"
on_release: app.root.current = "demoscreen1"
Use ids and reference through AppScreenManager aka the ScreenManager.
self.parent.ids.screen2.screen2_label = new_label
See full example below.
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import StringProperty
class DemoScreen1(Screen):
def ChangeScreen(self, button_text):
if button_text == "Button 1":
new_label = "This is the new label when button 1 is pressed"
print('Palim')
#DemoScreen2.LabelUpdater(new_label)
self.parent.ids.screen2.screen2_label = new_label
else:
new_label2 = "This is the new label when button 2 is pressed"
self.parent.ids.screen2.screen2_label = new_label2
#DemoScreen2.LabelUpdater(new_label2)
self.parent.current = "demoscreen2"
class DemoScreen2(Screen):
screen2_label = StringProperty("Test")
def LabelUpdater(NEW_LABEL):
screen2_label = StringProperty(NEW_LABEL)
class AppScreenManager(ScreenManager):
pass
class Tester(App):
pass
if __name__ == '__main__':
Tester().run()
kv
AppScreenManager:
DemoScreen1:
id: screen1
DemoScreen2:
id: screen2
<DemoScreen1>:
name: "demoscreen1"
orientation: "vertical"
GridLayout:
rows: 2
Button:
id: Button1
text: "Button 1"
on_release: root.ChangeScreen(Button1.text)
Button:
id: Button2
text: "Button 2"
on_release: root.ChangeScreen(Button2.text)
<DemoScreen2>:
name: "demoscreen2"
orientation: "vertical"
GridLayout:
rows:2
Label:
text: root.screen2_label
Button:
text:"Back"
on_release: app.root.current = "demoscreen1"
I've been at this for hours now, trying every solution I could find on here and trying random things....
I'm trying to build a layout consisting of 3 buttons at the top, then a scroll-able GridLayout or BoxLayout. I just cant figure out whats wrong... I've read on one response "bind the layout's size to adapt itself:" but I'm using screenmanagement and cant figure out how to do that with my code setup
<HomeScreen>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,.1
orientation: "horizontal"
Button:
text:"1"
Button:
text:"2"
Button:
text:"3"
ScrollView:
GridLayout:
orientation: "vertical"
size_hint_y: None
row_default_height: 60
cols:1
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Your code is correct, you just need to specify the height of the GridLayout. You can use height: self.minimum_height.
Reproducible example:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
kv_text = '''
<MyScreenManager>:
HomeScreen:
<HomeScreen>:
BoxLayout:
orientation: "vertical"
BoxLayout:
size_hint: 1,.1
orientation: "horizontal"
Button:
text:"1"
Button:
text:"2"
Button:
text:"3"
ScrollView:
GridLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height #<<<<<<<<<<<<<<<<<<<<
row_default_height: 60
cols:1
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
Button:
'''
class MyScreenManager(ScreenManager):
pass
class HomeScreen(Screen):
pass
class MyApp(App):
def build(self):
return HomeScreen()
def main():
Builder.load_string(kv_text)
app = MyApp()
app.run()
if __name__ == '__main__':
main()
Output:
I tried like #Ishinomori to recreate the app of #FJSevilla in pure Python3.7.
After a few changes I have done it!
# -*- coding: utf-8 -*-
# Kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.core.window import Window
class HomeScreen(BoxLayout):
def __init__(self, **kwargs):
# Initiate Box Layout and change orientation to vertical
super().__init__(**kwargs)
self.orientation = "vertical"
# Top Bar with Buttons "1", "2" & "3"
self.top_bar = BoxLayout(orientation="horizontal", size_hint=(1, .1))
self.top_bar.add_widget(Button(text="1"))
self.top_bar.add_widget(Button(text="2"))
self.top_bar.add_widget(Button(text="3"))
# Create the Gridlayout for the Scroll View and add height bounding
self.contend_scroll_view = GridLayout(size_hint_y=None, row_default_height=60, cols=1)
self.contend_scroll_view.bind(minimum_height=self.contend_scroll_view.setter('height'))
# 30 Dummy Buttons (real data here!)
for _ in range(30):
self.contend_scroll_view.add_widget(Button())
# Add the contend to the Scroll View
self.scroll_view = ScrollView()
self.scroll_view.add_widget(self.contend_scroll_view)
# Add the two Widgets to Home Screen
self.add_widget(self.top_bar)
self.add_widget(self.scroll_view)
class MyApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
# Only runs if file is executed directly, but not if importet
MyApp().run()
In my RootWidget I have a label and two buttons. I want to dynamically change the text of the label whenever one of the buttons is clicked.
Here's a minimal working example of how I do it at the moment.
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
w = Builder.load_string('''
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
Button:
text: '1'
on_press: label.text = self.text
Button:
text: '2'
on_press: label.text = self.text
''')
class RootWidget(BoxLayout):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()
Obviously I want to refactor the line on_press: label.text = self.text. My first tries ended in
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
MyButton:
text: '1'
MyButton:
text: '2'
<MyButton>:
on_press: label.text = self.text
But obviously the MyButton-class doesn't know the property label of the RootWidget-class. And class rules inside class rules is also not allowed.
Is there a way of accomplishing binding the on_press-action dynamically?
You can refer to the Label like this:
<MyButton#Button>:
on_press: self.parent.ids.label.text = self.text
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
MyButton:
text: '1'
MyButton:
text: '2'
It's quite simple actually, because through kv you can access parent of a widget/rule easily with self.parent, so your code would look like:
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
w = Builder.load_string('''
<RootWidget>:
id: root_widget
Label:
id: label
text: 'Push a button'
But:
text: '1'
But:
text: '2'
<But>:
on_press: self.parent.label.text = self.text
''')
class RootWidget(BoxLayout):
pass
class MainApp(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
MainApp().run()