I have a GridLayout which takes some Card widgets that are basically RoundedRectangles and are supposed to be filled with specific information, the problem is that the GridLayout can take many cards and I want the user to scroll the widget. Intuitively, I defined it inside a ScrollView to scroll the gridlayout, however, the scrollview does not work. I suspect that the problem is that the gridlayout's height is not actually changing and not adapting to its children but how can I achieve this?
My Python code:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
Builder.load_file("design.kv")
class MyLayout(Widget):
pass
class MainApp(App):
def build(self):
return MyLayout()
if __name__ == "__main__":
MainApp().run()
My KV code:
#:kivy 2.0.0
<Card#Widget>:
size_hint_y: None
height: (self.parent.height - self.parent.padding[1] * 3) / 2
canvas:
Color:
rgba: 1, 1, 1, 1
RoundedRectangle:
size: self.size
pos: self.pos
radius: [5]
<MyLayout>:
ScrollView:
size: root.size
do_scroll_x: False
do_scroll_y: True
GridLayout:
id: song_menu
cols: 2
size_hint_y: None
height: self.parent.height
padding: 10
spacing: 10
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
size: self.size
pos: self.pos
Card:
Card:
Card:
Card:
Card:
Card:
EDIT: I forgot to mention that I have already tried height: self.minimum_height, however, an absurd thing happens. The gridlayout has a weird behavior and does not show up. Basically I get this in the terminal:
[CRITICAL] [Clock ] Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute
The GridLayout height isn't adjusting because you have constrained it with:
height: self.parent.height
Try replacing that with:
height: self.minimum_height
Related
I am trying to get a background around a label that fits the number of lines in the texted in it. For example, a label with text 'Line1\nLine1\nLine3' would have a larger Y dimension that just 'line1'
What currently happens is all of the labels are the same size and clip-off text that doesn't fit within them, the labels are also inside a recycleview layout because I would like to be able to scroll and update large amount of the labels often.
I have tried a few things but have had no luck, and am struggling to get a variable to be understood where I have added # HERE in the .kv file
from kivy.app import App
from kivy.properties import NumericProperty, Clock, ObjectProperty, StringProperty
from kivy.uix.widget import Widget
from kivy.uix.recycleview import RecycleView
from kivy.uix.button import Button
from kivy.uix.label import Label
class TopPostsTest(RecycleView):
def __init__(self, **kwargs):
super().__init__(**kwargs)
message_height = NumericProperty(20)
items = ["hello\ntest\ntest", "test, gghgjhgjhgjhgjhghjghjgjhgjhgjhgjhgjhgjhgjhgjhg", "cheese"]
self.data = [{'text':str(p)} for p in items]
class LabelColor(Label):
pass
class TruthApp(App):
# def build(self):
# return super().build()
pass
if __name__ == "__main__":
TruthApp().run()
<MainControl#PageLayout>:
border: "25dp"
swipe_threshold: 0.4
TopPostsTest:
Settings:
<LabelColor>:
color: 0,0,0,1
text_size: self.size
halign: 'left'
valign: 'top'
font_name: "Assets/Fonts/Nunito-Bold.ttf"
font_size: "12dp"
multiline: True
canvas.before:
Color:
rgba: (0.8, 0.8, 0.8, 1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [5, 5, 5, 5]
canvas:
Color:
rgba:0,0.9,0.9,1
Line:
width:0.8
rounded_rectangle:(self.x,self.y,self.width,root.height, 5) # HERE
<TopPostsTest>:
viewclass: 'LabelColor'
scroll_y: 1
RecycleBoxLayout:
id: message_view
default_size: None, dp(40) # NewHERE
default_size_hint: 1, None
size_hint_y: None
padding: ["10dp", "16dp"]
spacing: "8dp"
height: self.minimum_height
orientation: 'vertical'
Thank you for any help :)
Edit:
I have found that I have been changing the wrong value and that the variable that needs changing has been marker with # NewHERE, however I am still unable to get it to work or get a variable from the py file into the kv
In order to get a Label that expands vertically as it's text-content grows you can set its height to its texture height.
Also, to fit the text within available space (width) you can change text_size.
Thus you can modify the kvlang for LabelColor as,
<LabelColor>:
color: 0,0,0,1
text_size: self.width, None
size_hint_y: None
height: self.texture_size[1]
In a button i have made a rounded button with canvas.before, and it changes colors as it should. The line is:
canvas.before:
Color:
rgba: btn_color_not_pressed if self.state=='normal' else btn_color_pressed
RoundedRectangle:
size: self.size
pos: self.pos
radius: [40]
The variables btn_color_not_pressed and btn_color_not_pressed are made with #:set in the start of the kv-file
I have tried to target the line with self.canvas.before.Color.rgba, as i am used to normally, but i get following error:
AttributeError: 'kivy.graphics.instructions.CanvasBase' object has no attribute 'Color'
How do i target that line from within kv and replace the variables ... or if necessary from the python file.?
How do i target the source: "some_file.jpg under Rectangle?
My goal is that when a user has clicked an option all the button colors (and maybe the background) in the app must change.
You cannot change the variables created in kv. Once your app is running, those variables no longer exist. You can, however, use a Property that is created in kv (or python) as an attribute of a class (or the App itself). Such Properties continue to exist while the App is running, and kivy recognizes such Properties and will automatically handle changes to those Properties. An example is to create a new class that extends Button and has Properties like you want:
<-MyButton#Button>:
# create the desired properties
btn_color_not_pressed: [.5, .5, .5,1]
btn_color_pressed: [.25, .25, .25, 1]
canvas:
Color:
# reference the above properties
rgba: self.btn_color_not_pressed if self.state=='normal' else self.btn_color_pressed
RoundedRectangle:
size: self.size
pos: self.pos
radius: [40]
# this is copied from style.kv to show the Button text
Color:
rgba: 1, 1, 1, 1
Rectangle:
texture: self.texture
size: self.texture_size
pos: int(self.center_x - self.texture_size[0] / 2.), int(self.center_y - self.texture_size[1] / 2.)
# actually make an instance of the new MyButton class
MyButton:
btn_color_not_pressed: [1,0,0,1]
btn_color_pressed: [0,1,0,1]
text: 'Button Test'
The <-MyButton#Button> creates a new class (MyButton) that extends Button. The prepended - indicates that the default canvas instructions for Button are not to be used and the provided instructions are used instead. Those new Properties can be changed in python code as usual. You can use a similar approach for the source property.
That brought me a step closer.
My problem now is that the colors only change the button itself or togglebutton-group, but only when you click on them. It only reacts to the new colors when activated (button og group).
The design is not updated
I tried the solution with the - but it made no difference
main.py
import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.factory import Factory
kivy.require("1.11.1")
class Controller(BoxLayout):
def __init__(self):
super(Controller, self).__init__()
ToggleButtonBehavior.allow_no_selection = False
def change_button_color(self,theme):
if theme == "red":
Factory.My_tgl_btn.btn_color_pressed = (1,0,0,.7)
Factory.My_tgl_btn.btn_color_not_pressed = (1,0,0,.5)
else: # theme == "blue":
Factory.My_tgl_btn.btn_color_pressed = (0,0,1,.7)
Factory.My_tgl_btn.btn_color_not_pressed = (0,0,1,.5)
class mainApp(App):
def build(self):
return Controller()
if __name__ == "__main__":
mainApp().run()
main.kv
#:set bg_color 1,1,1,.7
#:set txt_color 0,0,0,1
#:import Factory kivy.factory.Factory
<Controller>
BoxLayout:
orientation: "vertical"
background_color: 1,1,1,.5
background_normal: ""
Label:
text: "THIS IS THE MAIN TEKST"
color: txt_color
size_hint_y:.7
BoxLayout:
size_hint_y: .15
My_tgl_btn:
text: "RED theme"
group: 1
state: "down"
on_press: root.change_button_color("red")
on_release: root.change_button_color("red")
My_tgl_btn:
text: "Blue theme"
group: 1
on_press: root.change_button_color("blue")
on_release: root.change_button_color("blue")
BoxLayout:
size_hint_y: .15
My_tgl_btn:
text: "Option1"
group: 2
state: "down"
My_tgl_btn:
text: "Option2"
group: 2
state: "normal"
<My_tgl_btn#ToggleButton>
btn_color_pressed: 1,0,0,.7
btn_color_not_pressed: 1,0,0,.5
color: txt_color
background_color: 0,0,0,0
background_normal: ""
canvas.before:
Color:
rgba:self.btn_color_not_pressed if self.state=='normal' else self.btn_color_pressed
RoundedRectangle:
size: self.size
pos: self.pos
radius: [40]
Found a solution (here: Kivy: resetting toggle buttons to "normal" on re-entering screen)
It's kind'a ugly, but it works..
Give every button an id ... and then use on_enter for each button and set and change the state.
In the code above it would mean:
on_enter:
button1.state = "down"
button1.state = "normal"
button2.state = "down"
button2.state = "normal"
button3.state = "down"
button3.state = "normal"
button4.state = "down"
button4.state = "normal"
It works ... but it is not pretty :|
I am new to kivy. I want to insert a text into a kivy label at the startup. But the text of the lable shows out of the label as shown below. I can't find a way to fix this. So please give me a solution.
This is the code of the kv file.
<SmoothLabel#Label>
background_color: (0,0,0,0)
background_normal: ''
back_color: (255,255,255,1)
border_radius: [18]
canvas.before:
Color:
rgba: (255,255,255,0.3)
RoundedRectangle:
size: 50,50
pos: 100,10
radius: self.border_radius
<Money_Manager>
FloatLayout:
size_hint_y: None
height:100
Image:
source:'image4.png'
size: self . texture_size
allow_stretch: True
keep_ratio: False
SmoothLabel:
d: Total_Wealth
text: "Total_Wealth"
This is the code of the python file.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.image import Image
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_file('total_wealth.kv')
class Money_Manager(App, FloatLayout):
def build(self):
return self
Money_Manager().run()
In your kv file, you have set the pos and size of the RoundedRectangle to fixed values. You need to set them to the pos and size of the Label. So change this:
<SmoothLabel#Label>
background_color: (0,0,0,0)
background_normal: ''
back_color: (255,255,255,1)
border_radius: [18]
canvas.before:
Color:
rgba: (255,255,255,0.3)
RoundedRectangle:
size: 50,50
pos: 100,10
radius: self.border_radius
to:
<SmoothLabel#Label>
background_color: (0,0,0,0)
background_normal: ''
back_color: (255,255,255,1)
border_radius: [18]
canvas.before:
Color:
rgba: (255,255,255,0.3)
RoundedRectangle:
size: self.size
pos: self.pos
radius: self.border_radius
Here is my answer for yours
# import kivy module
import kivy
# this restricts the kivy version i.e
# below this kivy version you cannot use the app or software
kivy.require("1.9.1")
# base Class of your App inherits from the App class.
# app:always refers to the instance of your application
from kivy.app import App
# if you not import label and use it it through error
from kivy.uix.label import Label
# defining the App class
class MyLabelApp(App):
def build(self):
# label display the text on screen
lbl = Label(text ="Label is Added on screen !!:):)")
return lbl
# creating the object
label = MyLabelApp()
# run the window
label.run()
Output:
I'm trying to set the background of my layout with the following kv language:
<RootWidget>:
Menu:
orientation: 'vertical'
Button:
text: 'btn1'
on_release: print('btn1')
Button:
text: 'btn'
on_release: print('btn2')
BoxLayout:
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'main_background.png'
ToggleMenuButton:
on_release: root.toggle_state()
size_hint: None, None
size: 60, 60
background_color: 0, 0, 0, 0
Image:
size: self.parent.size
pos: self.parent.pos
allow_stretch: True
source: './data/images/white_menu.png'
And this is my code:
from kivy.app import App
from kivy.garden.navigationdrawer import NavigationDrawer
from layouts.menu import Menu
from layouts.menu import ToggleMenuButton
from layouts.content import Content
class RootWidget(NavigationDrawer):
pass
class MainApp(App):
def build(self):
self.root = RootWidget()
return self.root
if __name__ == '__main__':
MainApp().run()
The Menu and Content classes are Kivy BoxLayout, the ToggleMenuButton is a Kivy Button.
When I run this code I get the following screen:
The background should be my image, not black. If I but the white_menu.png as my background, I also get a black braground. But, if put the following code:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'main_background.png'
I get the following:
Like you can notice on the bottom, the image is transparent, with 3 white rectangles, but on the main layout the image is red. I kwnow that it is red, because the rgba code, but why the image changes it's color?
If I put the background on canvas.after it works correctly, I can see my background, but I can't see the widgets.
Summing up, I want to understand what is happening? Why I cant see the changes on canvas.before? Why/How putting a color on my canvas changes my image?
If I change, the RootWidget to be a BoxLayout instead of a NavigationDrawer, it works correctaly. Why? Why changing the canvas of a child of a child of a NavigationDrawer does not work?
I'm trying to copy this gif, which is done in Kivy (here's the link to the full page )
Just as I started, I noticed a black screen between transition (link to what it looks like so you don't have to copy-paste and run)
Why does that black screen appear?
EDIT: I must work without buttons.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# Create both screens. Please note the root.manager.current: this is how
# you can control the ScreenManager from kv. Each screen has by default a
# property manager that gives you the instance of the ScreenManager used.
Builder.load_string("""
<MenuScreen>:
canvas.before:
Color:
rgba: 122,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
<SettingsScreen>:
canvas.before:
Color:
rgba: 0,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
""")
# Declare both screens
class MenuScreen(Screen):
def on_touch_down(self, touch):
sm.current = 'settings'
class SettingsScreen(Screen):
def on_touch_down(self, touch):
sm.current = 'menu'
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
class TestApp(App):
def build(self):
return sm
if __name__ == '__main__':
TestApp().run()
EDIT: I've tried this but still not working
<sm>:
canvas:
Color:
rgb: (0, 255, 255)
Rectangle:
size: self.size
pos: self.pos
You are not supposed to use the Screen subclasses directly. Instead you must add a component first (e.g. Button or Layout), for example use RelativeLayout:
Builder.load_string("""
<MenuScreen>:
RelativeLayout:
canvas.before:
Color:
rgba: 122,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
<SettingsScreen>:
RelativeLayout:
canvas.before:
Color:
rgba: 0,255,0,2
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'hello'
""")
That black area is a canvas of the screen manager. If you don't like it black, then you can paint it, just like you did with screens; or change transition type to NoTransition to hide it.
Also, you should consider building your screen manager inside that kv lang string.
Old, but in case anyone runs into this issue:
To clarify the vague responses in the comments, you need to paint your screen manager the same way you would paint a screen.
Example in kvlang:
ScreenManagement:
canvas.before:
Color:
rgba: 1,1,1,1
Rectangle:
size: self.size
pos: self.pos
id: screen_manager
transition: NoTransition()
Screen1:
Screen2:
Settings_: