I'm using Kivy with Python 2.7. I am familiar with how to alter the color of the static button itself, but how do you change the color of the button when you press it? The default is blue.
Thanks for any help.
According to the reference for Button, the property background_down stores the path to an image used for the background while the Button is pressed. This is the default:
background_down = StringProperty(
'atlas://data/images/defaulttheme/button_pressed')
You can change that property to point to a different image oratlas.
The Kivy framework uses background images for button_normal and button_down, which the background_color only tints, so this in the kv language might not behave how you'd expect:
<Button>:
background_color: 1, 0, 0 # Tints the button red
background_normal: 'images/button_normal.png' # A clear image gives a bright red.
background_down: 'images/button_down.png' # A gray image gives a duller red.
border: (2, 2, 2, 2) # Don't stretch the outer two pixels on each edge when resizing.
This style lets you have say a dull border and bright inner and swap them round on the button press.If you use this system though, images will be imported with colours ignored. To fix this and solve your problem remove the background_color:
<Button>:
background_normal: 'images/button_normal.png' # Eg. A red button
background_down: 'images/button_down.png' # Eg. A green button
border: (2, 2, 2, 2) # Don't stretch the outer two pixels on each edge when resizing.
That'll change the colour of the buttons to whatever you've made in the image. It's worth noting that Kivy is excellent at stretching the images out, so if you have single colour buttons or tiny borders, you only need a tiny image, I use 8x8 pixels.
Related
I was wondering if there is a way to get a static background in a Kivy application with a screen manager. With static, I mean that the background remains as it is, even when switching screens. I am using a .kv file for the layout. I'd guess it has something to do with the placement order within a .kv file.
Thanks!
You can use a float layout as the root widget in every screen and add to that float layout the image and the other layout in your screen, here is a code example in kv:
Screen: # Screen 1
id: Home
FloatLayout:
Image:
source: "path to the image"
BoxLayout: # Here you put your other layout
# And here the code you had
Screen: # Screen 2
id: Another Screen
FloatLayout:
Image:
source: "path to the image"
BoxLayout: # Here you put your other layout
# And here the code you had
This is the solution that I know, it might not be perfect for you, but I will leave the other options to others...
FloatLayout:
Image:
ScreenManager:
would be enough I think but be careful, if the transition of ScreenManager is ShaderTransition(or a sub-class thereof), it doesn't respect the pixels in the background, so the animation during the transition may not work properly.
I'm new to Kivy and having trouble specifying the background color of a Button. Here's my simple example:
# custombutton.py
from kivy.app import App
from kivy.uix.widget import Widget
class MyWidget(Widget):
pass
class CustomButtonApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
CustomButtonApp().run()
And the accompanying kv file custombutton.kv:
#:kivy 1.7.2
<MyWidget>:
canvas:
Color:
rgb: (0.93, 0.93, 0.93)
Rectangle:
pos: self.pos
size: self.size
Button:
center: self.parent.center
font_size: 14
height: 28
background_color: (1.0, 0.0, 0.0, 1.0)
text: "I'm a Button"
I'm sure I'm missing something obvious, but I've been messing with this for over an hour now and getting nowhere. The button seems to get colored a hint of very dark red:
Is this not the way to specify the background color for a Button in Kivy?
Thanks!
It's been a while since this was first posted so maybe with updates they came up with a better solution:
Button:
background_normal: ''
background_color: 1, .3, .4, .85
Since the Button has a default grey, adding background color will only tint the button. By setting background_normal to '' that resets the default to white. From the white canvas the background_color works as you would expect.
Documentation
1) https://kivy.org/docs/api-kivy.uix.button.html?highlight=button#module-kivy.uix.button
Ah, this is a common confusion. The problem is that Button.background_color really works as a kind of tint, not just a block colour. Since the default background is a grey image (the one you normally see if you make an unstyled button), what you end up seeing is a red tint to that grey image - which comes out as the dark red you observe.
You can get the behaviour you want by replacing the background image to just one that's plain white (it doesn't have to be more than a few pixels), or by otherwise playing with the background_normal and background_down properties. When your background_color tints the new pure white image, you get the pure red you're after.
I guess this isn't so clear in the docs, I'll try to improve it.
The problem with using background_normal = '' is that it blows away the kivy button image with raised edges/shadowing and setback. One way to get a raised button of a specific color would be to make your own atlas using gimp or other editors. However, by some trial and error I found that the Button background_color attribute can implement a tint as well as the documented shade. In other words, it can have 'RGB' values > 1. So to get an exact button color with the kivy button defaulttheme for raised edges, use:
import numpy as np
# the defaulttheme for kivy buttons is grey 88,88,88
kivy_defaulttheme_color = np.array([88,88,88,256])/256
desired_button_color = np.array([78,101,115,256])/256
tintshade_color = desired_button_color / kivy_defaulttheme_color
Button(background_color = tintshade_color)
which demonstrates both a tint and a shade.
I have a kivy program that has a ‘sqlite’ database containing details of a golf competition which displays information & warning messages via a standard Pop-up function - pop_mess()
For testing purposes I open an alternative ‘testing’ database in a function – set_test_db(). When this is done I would like to change the background colour of the pop-up to warn the user that the main database is not being used.
I have tried to do this (code at end of 'set_test_db'). However although the routine retrieves the current background colour correctly it will not change it.
Can someone point me in the direction of a solution. The coding method works fine for changing the text properties of a widget. (similar questions usually refer to text properties). I have used both list & tuple for colour values.
In .ky file
<CustomPopup>:
popup_message: popup_message
size_hint: .98, .75
id: popup_id
title: "Casual and Ancient"
title_align: 'center'
title_color: 1,.5,.3,1
BoxLayout:
id: contentbox
orientation: 'vertical'
TextInput:
id: popup_message
color: .3,.4,1.0,1
background_color: [.7,1.0,.2,1]
text: "text message goes here"
font_size: 16
font_name: 'RobotoMono-Regular'
…..
CaaRoot:
<CaaRoot>:
orientation: 'lr-tb'
padding: 10
etc
...
in main.py
(self = CaaRoot)
...
def pop_mess(self,message):
p_up=CustomPopup()
lab=p_up.ids['popup_message']
lab.text=message
p_up.open()
...
def set_test_db(self):
# on button in CaaRoot
# open test data base instead of real
if self.db != None:
print 'Cannot Open Test Data Base - already running'
else:
# open ‘test’ database
# set background colour of popup to warn user.
fn='/home/.... caatestdb.db
self.op_db(fn)
self.testing='TEST database'
p_up=CustomPopup()
lab=p_up.ids['popup_message']
x=lab.background_color
new_col=[.9,.3,.3,1]
lab.background_color=new_col
self.pop_mess('color set to : '+str(new_col)+ ' was : '+str(x))
the popup displays
color set to : [0.9, 0.3, 0.3, 1] was : [0.7, 1.0, 0.2, 1]
(with background still set to [0.7, 1.0, 0.2, 1])
Have a look at kivymd, which solves issues like that very well. It is basically a very elegant template for kivy based on Google's Material Design.
For me it makes all the coloring stuff and appearance in general much easier, especially as I am not a designer but a programmer.
The Demo has also a very complete sets of examples including popups.
On further investigation of my problem I came across the similar question (Changing Background of Kivy Popup )
The fact that the solution given to that was “non-intuitive” involving the properties of the ‘inherited’ Canvas element(?) made me realise (remember ?) than some widgets do not have the full range of modifiable properties.
The 'background_color' property that I was retrieving was a property inherited from the Canvas which cannot be easily changed.
I have avoided the problem by creating a ‘popup’ widget whenever I wish to use it rather than modifying an existing widget. I still cannot (simply) change the background_color of the popup but I can change the background color of the parent element so that the user’s eye immeditely sees something different. I can also change the text color when calling the popup message function.
def pop_mess(self,message,colour=(.9,.6,.9,1)):
#
# text color can ve overridden by the 'colour' parameter
# self.backc can be set as required to set the color behind the
# popup depending on circumstances
popup=Popup(title='Casual & Ancient', \
content=Label(text=message,color=colour,\
font_name = 'RobotoMono-Regular'),\
size_hint=(.98,.75),background_color=self.backc)
popup.open()
I'm not completely sure that the analysis is correct or that there may be some side-effects but it seems to work ok (and was somewhat simpler to code)
I am making a BoxLayout widget (orientation = 'horizontal') that contains three widgets inside of it, a label, a text box, and a check box.
thisRow = BoxLayout(orientation='horizontal')
l = Label(text='Enter plate 1:\n(Plate #)')
t = TextInput(text = 'this is a text box')
c = CheckBox()
thisRow.add_widget(l)
thisRow.add_widget(t)
thisRow.add_widget(c)
This produces the following widget (thisRow):
After the box is checked...
The rightmost black box is actually the checkbox, and works functionally, however there is no way for the user to know that it is in fact a checkbox. I would expect a smaller empty square in the middle, as is depicted in pictures here.
How do i get the traditional checkbox image (smaller empty square box)? Or generally, how can I make it more obvious that the box is a check box and not just an empty label?
Thank you
This is really interesting question and Malonge tried it in a good way. Right now(1.9.2-dev) there is still fixed size on CheckBox's well, call it a background. It's an image that Widget takes from atlas and changes if the state changes. Therefore until now there was no clear way how to do it. Here is an example. Soon on master there'll be CheckBox(color=[r,g,b,a]) option. Thanks ;)
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.boxlayout import BoxLayout
Builder.load_string('''
<CheckBoxBG>:
Label:
TextInput:
CheckBox:
canvas.before:
Color:
rgb: 1,0,0
Rectangle:
pos:self.center_x-8, self.center_y-8
size:[16,16]
Color:
rgb: 0,0,0
Rectangle:
pos:self.center_x-7, self.center_y-7
size:[14,14]
''')
class CheckBoxBG(BoxLayout):pass
runTouchApp(CheckBoxBG())
Looks like the smaller check box is hidden when the background color is black. Here is an example of a red background.
It's not ideal because I do like the black background, but I can run with it for now. If anyone knows how to do this with a black background that would be great. Thank you
Alternatively, to change your checkboxes background, you can use another image from the atlas or create images and then load them:
mycheckbox= CheckBox(
background_checkbox_normal ='tlas://data/images/defaulttheme/button_disabled'
background_checkbox_down = 'my_checkboxes_checked.png'
)
In Kivy 1.9.2.dev0 (and apparently since version 1.9.0) you can change the background image of checkboxes. By default, Kivy uses for these backgrounds from the atlas*.
background_checkbox_normal = StringProperty('atlas://data/images/defaulttheme/checkbox_off') #when the checkbox is not active.
background_checkbox_down = StringProperty('atlas://data/images/defaulttheme/checkbox_on') # when the checkbox is active.
background_checkbox_disabled_normal = StringProperty('atlas://data/images/defaulttheme/checkbox_disabled_off') #when the checkbox is disabled and not active.
background_checkbox_disabled_down = StringProperty('atlas://data/images/defaulttheme/checkbox_disabled_on') #when the checkbox is disabled and active.
You can have a look here at all the attributes :
*The atlas is a package of multiple textures that reduces the number of images loaded and speedup the application loading. You have see a preview of the atlas in Python Install Folder\Lib\site-packages\kivy\data\images\defaulttheme-0.png
I am creating a 'Map editor' using 64x64 tiles. I need my canvas to update when i click and change the tile at location. I originally set the canvas with
with self.canvas:
Rectangle(source = 'image.png')
in one class, then in my touch_down class i call
with self.canvas:
Rectangle(source = 'newImage.png')
after i change my image to update it.
I have been able to get it to update but i have to create a new image each time, it seems it wont update since i already added that rectangle with that specific source image, and doesn't see that the image has been changed?
In response to Ryan P
Still nothing. I tried this
Class mypaintwidget(Widget): #This is added as a widget to my layout
def on_touch_down(self, touch):
with self.canvas:
self.rect = Rectangle(source = 'image.png')
tilepng = pil.open('64x64tile.png') #pil is Python Image Library
tilemap = pil.open('image.png')
tilemap.paste(tilepng,location)
tilemap.save('newimage.png')
self.rect.source = 'newimage.png'
It will only update ONCE. then nothing (but will still save that image, but wont show it to me.
You need to modify the Rectangle instruction (or remove and replace, but modifying is easier):
with self.canvas:
self.rect = Rectangle(source='image.png')
Then later:
self.rect.source = 'newImage.png'
As for the second part of your question, the issue is that images are cached upon loading in Kivy. So when you save newimage.png and reload it again, Kivy knows you've already loaded newimage.png. This isn't really a good design for a Kivy app.
Also, creating the Rectangle in on_touch_down means you end up creating a new Rectangle each time the widget is pressed, so you just add more and more unnecessary drawing instructions.
You may also notice that when you add multiple of these widgets to a layout, that they all render in the same place at the full size of the window. Widgets are not constrained to drawing in their area, and can draw anywhere within the app. You need to make sure the Rectangle instruction knows where to draw, by passing it size and pos parameters.
Finally, instead of saving out the image, you can just display one atop the other. This will be much more efficient and doesn't require using unique filenames or messing with the caching system.
You should definitely take a look at the Kivy language (kv) as it is much easier to use for designing widgets and laying out your app.
Here's an example of using kv to do this:
<TileWidget>:
tilesource: ''
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
size: self.size
pos: self.pos
source: 'image.png'
Color:
a: 1 if self.tilesource else 0
Rectangle:
size: self.size
pos: self.pos
source: self.tilesource
Now I'll explain what all of this does.
<TileWidget>:
This is a class rule, because the name is surrounded by <>. It will apply to all instances of TileWidget.
tilesource: ''
Here we are setting the value of the property tilesource. But tilesource isn't a property on the Widget class! No worries. In kv, when you assign a value to a non-existent property like this, the property will be created automatically. Since the value we're assigning to the property is a string, Kivy will realize we want this to be a StringProperty.
By creating this property, we are making it possible to affect this widget from the outside. We will use the value of this property to display an image later.
canvas:
Just like using with self.canvas: from the class in Python.
Color:
rgba: 1, 1, 1, 1
Inside a canvas block, we can add drawing instructions. We start with a Color instruction, because you don't know what the color is currently set to. This color will tint the images we display, and we don't want any tint, so we use a full white color.
Rectangle:
size: self.size
pos: self.pos
source: 'image.png'
Now we are going to render the first image. We make sure that the Rectangle's size and pos match the widget. In kv, doing this automatically creates a binding. If the widget is moved around, its size will change, and the Rectangle will update itself to match.
Color:
a: 1 if self.tilesource else 0
This time, we know what the color is set to. However, we don't want to draw anything else until an image has been set. In kv, properties will accept any Python value. This means you can use ternary expressions like this one, or function calls, or arithmetic, etc. So if self.tilesource evaluates to False (like an empty string, the default value we set above) then the a property of the color (the alpha component) will be set to 0, otherwise it will be 1.
Rectangle:
size: self.size
pos: self.pos
source: self.tilesource
Finally, we render the selected image.
Now, to create the widget itself, we just need a little bit of Python:
class TileWidget(Widget):
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.tilesource = '64x64tile.png'
return True
return super(TileWidget, self).on_touch_down(touch)
It's important to use collide_point when handling touches. Just like how widgets can draw anywhere in the app, they can also handle touches anywhere in the app. This makes sure that the touch is actually within the bounds of our widget before acting. Inside the collide_point block, we will return True to let Kivy know that we have handled this touch and that nothing else should process it. Otherwise, we call super() to let the default handler take over.