Combining image and text within a button in kivy - kivy

What's the preferred way to combine an image/icon and text within a button? For example, how would you create a button with text = 'my button', and a graphical icon to the left of that text?

Regarding to question #2.
The way Kivy works is embedding Widget instances. Since Image and Button are subclasses of Widget, then all you have to do is embed an Image inside a the Button. Notice that the positioning inside a widget is fixed. You have to give explicit coordinates.
That said, you can always embed a Layout to organize the stuff you are putting inside the Button.
Here is the simple ex
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<ButtonsApp>:
orientation: "vertical"
Button:
text: "B1"
Image:
source: 'kivy.png'
y: self.parent.y + self.parent.height - 200
x: self.parent.x
Label:
text: "A label"
""")
class ButtonsApp(App, BoxLayout):
def build(self):
return self
if __name__ == "__main__":
ButtonsApp().run()
EDIT: An example of how a relative layout can be embedded inside a button
In this case I am using a StackLayout to organize an Image and a Label inside. As I said, Button is a Widget and Kivy works embedding widgets inside widgets. It doesn't matter if they are labels, buttons or layouts.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<ButtonsApp>:
orientation: "vertical"
Button:
StackLayout:
pos: self.parent.pos
size: self.parent.size
orientation: 'lr-tb'
Image:
source: 'kivy.png'
size_hint_x: None
width: 74
Label:
size_hint_x: None
width: 100
text: "The text"
Label:
text: "A label"
""")
class ButtonsApp(App, BoxLayout):
def build(self):
return self
if __name__ == "__main__":
ButtonsApp().run()

Meanwhile, there is another way.
You can use icon fonts such as Font Awesome and combine them with text.
Either import the font directly and wrap the text in their font tag, or simply use some of the libraries that take care of that.
#: import icon ...
Button:
markup: True
text: "%s Comment" % icon('comment', 32)
size_hint_x: None
width: 100
Kivy-iconfonts converts css/tff combinations that are distributed for web sites into a json format that it loads during runtime using the import statement as shown in the example above.
I extended this in my fork to fetch Font Awesome icons during runtime and put them into the working directory of your application. That gives you the advantage of not having to distribute the fonts with the application.

Define a new Button class or modify the one in Kivy.uix.button
class MyButton(Button):
#add these three properties in the class
icon = ObjectProperty(None)
icon_size = (0,0)
icon_padding = NumericProperty(0) #Enter any default value like 50 if you will
#always use an icon, or specify this field
#while creating the button
def __init__(self, **kwargs):
#no changes here, just for reference
return super(MyButton, self).__init__(**kwargs)
KV file:
<MyButton>:
state_image: self.background_normal if self.state == 'normal' else self.background_down
disabled_image: self.background_disabled_normal if self.state == 'normal' else self.background_disabled_down
canvas:
Color:
rgba: self.background_color
BorderImage:
border: self.border
pos: self.pos
size: self.size
source: self.disabled_image if self.disabled else self.state_image
Color:
rgba: (1, 1, 1, 1) if root.icon != None else (1,1,1,0)
Rectangle:
source: root.icon
size: (self.texture_size[1],self.texture_size[1]) if self.icon_size == (0,0) else self.icon_size
pos: int(self.center_x - self.texture_size[0] / 2.)-dp(root.icon_padding), int(self.center_y - self.texture_size[1] / 2.)
Color:
rgba: 1, 1, 1, 1
Rectangle:
texture: self.texture
size: self.texture_size
pos: int(self.center_x - self.texture_size[0] / 2.)+dp(root.icon_padding), int(self.center_y - self.texture_size[1] / 2.)
Now just create a button widget
MyButton(text = 'Hello',icon = 'icon.png', icon_padding = 50)

Related

why is kivy button shadow not appearing?

I've been trying this:Image with rounded corners and shadow Kivy and anyway I've made a drop shadow in GIMP or GNU Image Manipulation Program, The image is called 1.png
1.png
I'm trying to make the shadow appear
and I've tried the link above's code:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<RoundedButtons>:
orientation: 'vertical'
Button:
text: '[color=3333ff][b]Rounded Button 1[/b][/color]'
markup: True
background_normal: '1.png'
background_down: '1.png'
border: 30,30,30,30
Button:
text: '[color=ff3333][b]Rounded Button 2[/b][/color]'
markup: True
background_down: '1.png'
background_normal: '1.png'
border: 30,30,30,30
""")
class RoundedButtons(BoxLayout):
pass
class TestApp(App):
def build(self):
return RoundedButtons()
if __name__ == "__main__":
TestApp().run()
and this the output
output
I'm not really getting what I wanted as my output, I've got confused because when I was viewing the image on my computer its showing a pattern in the background - I knew that the image had a deleted/invisible background -
here's how it looks like:
viewed image
I'm running it on a Chromebook.
Your drop shadows are there, they are just hard to see with a dark background. Try changing your kv to:
<RoundedButtons>:
orientation: 'vertical'
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Button:
text: '[color=3333ff][b]Rounded Button 1[/b][/color]'
markup: True
background_normal: '1.png'
background_down: '1.png'
border: 30,30,30,30
Button:
text: '[color=ff3333][b]Rounded Button 2[/b][/color]'
markup: True
background_down: '1.png'
background_normal: '1.png'
border: 30,30,30,30
Adding the white background to the RoundedButtons makes the shadow more obvious.

Recreating kivy app with scrollview to be used as screen with screenmanager

I've been trying to use scrollview in a screen, that will be used as part of an app and I managed to find the following code that creates an app with scrollview. But I'm not able to change it into screen class.
Here is the python code:
from kivy.app import App
from kivy.uix.button import Button
class ScrollButton(Button):
pass
class TestApp(App):
def build(self):
super(TestApp, self).build()
container = self.root.ids.container
for i in range(30):
container.add_widget(ScrollButton(text=str(i)))
return self.root
if __name__ == '__main__':
TestApp().run()
.kv file
ScreenManager:
Screen:
ScrollView:
size_hint: None, None
size: 600, 320
pos_hint: {'center_x': .5, 'center_y': .5}
GridLayout:
cols: 1
padding: 10
spacing: 10
height: self.minimum_height
size_hint: None, None
do_scroll_x: False
id: container
<ScrollButton>
size_hint: None, None
size: 600, 40
I would appreciate if anybody could show how to do it/give me some directions on how to change this to fit my purpose. Any alternative way of doing this is also welcome.
Many people recommend making the ScreenManager in the build method in the python file instead, here's how to do that:
Python
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
Builder.load_file("styling.kv")
class ScrollButton(Button):
pass
class MainScreen(Screen):
def on_kv_post(self, instance):
container = self.ids['container']
for i in range(30):
container.add_widget(ScrollButton(text=str(i)))
class TestApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainScreen())
return sm
if __name__ == '__main__':
TestApp().run()
Kivy
<ScrollButton>:
size_hint: None, None
size: 600, 40
<MainScreen>:
name: 'MainScreen'
ScrollView:
size_hint: None, None
size: 600, 320
pos_hint: {'center_x': .5, 'center_y': .5}
GridLayout:
cols: 1
padding: 10
spacing: 10
height: self.minimum_height
size_hint: None, None
do_scroll_x: False
id: container
Changes made
ScreenManager made in .py instead of .kv
Created Screen class in .py, so we can use python methods from the Screen
Widget adding moved to the Screen's class, using on_kv_post (this method is fired only one time when the Screen is ready)
Made the screen a template class instead of an object in .kv which the Screen in the python file uses to make the final Screen
PS: This is how it's usually done

Kivy Layout Issue

I am trying to create a GUI using Kivy. However, I cannot resolve some formatting issues.
Here is a slimmed down version of my KV file:
BoxLayout:
MainCanvas:
size_hint: 1,1
size: (root.width,root.height*.9)
DoubleEllipseWidget:
ActionBar:
id: _action
size_hint: 1,0.1
size: (root.width,root.height*.1)
pos_hint: {'bottom':1}
ActionView:
use_separator: True
ActionPrevious:
title: 'Test App:'
with_previous: False
ActionOverflow:
disabled: True
ActionButton:
important: True
text: 'Button 1'
#on_release: some_function
ActionButton:
text: 'Button 2'
#on_release: some_function
ActionButton:
text: 'Button 3'
#on_release: some_function
<DoubleEllipseWidget>
size: [200, 200]
canvas:
Color:
rgba: 0, 0, 0, 1
Ellipse
size: [198, 198]
pos: [600-200-100, 800-200-100]
Color:
rgba: 1, 1, 1, 1
Ellipse
size: [200, 200]
pos: [600-200-100, 800-200-100]
TextInput:
on_parent:self.focus = True
text: 'center of circle'
background_color: (0,0,0,0)
foreground_color: (0,0,0,1)
What I am trying to get to is very easily explained.
Essentially, there should be a menu bar running horizontally along the screen window (10% of total height and 100% of width). I believe I have done this.
The remaining 95% height should be the main canvas - I believe I have also done this.
The final part is getting a particular widget to be placed into the center of the main canvas. This is where I am stuck and would appreciate some help.
The widget I need to center is made up of two circles (one centered on top of the other, with one being slightly smaller than the other). Then, on top of the top-most circle should be a TextInput.
After taking the advice from this community, I stripped all of the logic back until I was left with just the basics (class definitions and layout), and was still having issues.
However, I have now learnt that what I assumed was a layout issue was likely not. A Kivy bug report last commented on in Nov 2018 (TextInput focus problem #3863), suggests there are unexplained instances where textinputs can lose focus. Below is the suggested workaround which also worked for me. Thanks for those that tried to help me.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
Builder.load_string('''
<Base>:
Button:
text: 'Press for Popup'
on_press: root.edit_text()
<TextInputX>
id: texter
keyboard_mode: 'managed'
is_focusable: True
focus: True
keyboard_on_key_down:
texter.show_keyboard()
''')
class Base(BoxLayout):
def __init__(self, **kwargs):
super(Base, self).__init__(**kwargs)
def edit_text(self, *args):
self.clear_widgets()
content = TextInputX()
# content.focus = True
popup = Popup(content=content)
popup.open()
class TextInputX(TextInput):
def __init__(self, **kwargs):
super(TextInputX, self).__init__(**kwargs)
# self.focus = True
class SampleApp(App):
def build(self):
return Base()
if __name__ == '__main__':
SampleApp().run()

Positioning images with FloatLayout

This is driving me insane. I'm trying to position an image to the top of the main window with FloatLayout. Below is a simplified example.
It seems that a button is fine, but an image defaults to a 100x100 square (yes, I think I've read that somewhere) and the top bounding box of the image is at the top of the screen, not the image.
How can I force the image (top of the rectangle) to the top of the window like the button is?
Screen_showing example_button_and_image
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.config import Config
Config.set('graphics', 'width', '480')
Config.set('graphics', 'height', '800')
class MyApp(App):
def setOrientation(self, orient):
""""""
self.orient = orient
def build(self):
return FloatLayout()
if __name__ == "__main__":
app = MyApp()
app.setOrientation(orient="vertical")
app.run()
<FloatLayout>:
Image:
source: 'image_400x90.png'
pos_hint: {'left':1, 'top':1}
size_hint: None, None
allow_stretch: False
keep_ratio: True
Button:
font_size: 30
color: 0,1,0,1
size_hint: 0.3, 0.1
text: "TopRight"
pos_hint: {'right':1, 'top':1}
That happend because the Image is a Widget and "the real image" (the picture) is a texture of that Widget, and by default, the image is centered and fits inside the widget bounding box. If you don’t want that, you can set allow_stretch to True and keep_ratio to False. (docs)
For understand that you can add a canvas with a rectangle in the Image Widget like this:
Image:
canvas:
Color:
rgba: 1, 1, 1, 0.5
Rectangle:
pos: self.pos
size: self.size
source: 'dog.jpg'
pos_hint: {'left':1, 'top':1}
size_hint: None, None
allow_stretch: True
keep_ratio: False
And then you can see why the image doesn't do what you want
another pic (the image(texture) is centered and fits inside the widget):
One thing you can do is set allow_stretch: True and keep_ratio: False
This is the result: (set the size with size_hint)
Favcau gives one solution above (and thanks), but I have found what I think is more obvious. All it needs is to have the size defined in the kv file.
Image:
id: image2
source: 'image_400x90.jpg'
pos_hint: {'left':1, 'top':1}
size_hint: None, None
size: 400, 90
allow_stretch: True
keep_ratio: False

kivy: How to change Switch default from ON/OFF to OPEN/CLOSE or to YES/NO?

To be more clearly, I want the text of the Switch changed from On/Off to Open/Close or Yes/No. I did not find out how to do it. Thanks.
You can hack this by changing the background image of the right rectangle in the Switch.
You can make an icon that is 83x32 pixels like the widget.
I Made a very ugly example on sumo an online photoeditor:
Then I saved it as images/icon.jpg
If you want to change the slider too, its 43x32 pixels.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.switch import Switch
class MyGui(Widget):
def __init__(self,**kwargs):
super(MyGui,self).__init__(**kwargs)
self.switch = Switch()
self.switch.canvas.children[2].source = "images/icon.jpg" # The slider is the last element, so in that case -> children[-1].source
self.add_widget(self.switch)
class MyApp(App):
def build(self):
return MyGui()
if __name__ == '__main__':
MyApp().run()
If you want to do this in kv language, you could do like this:
from kivy.lang import Builder
Builder.load_string("""
<MyGui>:
Switch:
canvas:
Color:
rgb: 1,1,1
Rectangle:
source: 'switch.jpg' # make or download your background jpg
size: sp(83), sp(32)
pos: int(self.center_x - sp(41)), int(self.center_y - sp(16))
Rectangle:
source: 'switch_slider.jpg' # make or download your slider jpg
size: sp(43), sp(32)
pos: int(self.center_x - sp(41) + self.active_norm_pos * sp(41)), int(self.center_y - sp(16))
""")
class MyGui(Widget):
pass
The switch text is sadly not a text but an image. You can find the kv file here. You should be able to provide a different theme (see here) or you can override the class and implement your own rendering (which then displays OPEN/CLOSE) instead of the ON/OFF.
If you don't intend to change its whole appearance, you can patch the style.kv and use a little bit of canvas instructions to create a widget of your taste. ^.^
(and it's a lot more flexible than creating image for each different switch, if you don't want to change its color)
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<Custom#Switch>:
values: ['OFF', 'ON']
canvas:
Color:
rgb: 0.2, 0.709, 0.898, 1
Rectangle:
size: [sp(41.5), sp(20)]
pos: [self.center_x - sp(41.5), self.center_y - sp(10)]
Color:
rgb: 0.4, 0.4, 0.4, 1
Rectangle:
size: [sp(41.5), sp(20)]
pos: [self.center_x, self.center_y - sp(10)]
Label:
text: '[b]{}[/b]'.format(root.values[0])
markup: True
font_size: 13
pos: [root.center_x - sp(70), root.center_y - sp(50)]
Label:
color: 0.75, 0.75, 0.75, 1
text: '[b]{}[/b]'.format(root.values[1])
markup: True
font_size: 13
pos: [root.center_x - sp(30), root.center_y - sp(50)]
<Test>:
Custom:
Switch:
Custom:
values: ['Yes', 'No']
''')
class Test(BoxLayout): pass
runTouchApp(Test())
If you're using #el3in's solution make sure to clarify canvas.after instead of canvas otherwise the original switch image stays on top ;)

Resources