Adding a kivy custom widget only adds the first layout - python

I am trying to add a custom widget for each item in a list.
The first one works fine, but every on following the first, only the first Layout is shown. (yellow square)
I have already tried to add_widget(Builder.load_string the entire Custom Widget, leading to the same result.
The Layout class referenced to the custom widget:
class StockPortfolio(FloatLayout):
pass
The function to add the widgets:
portfolio_list = [1, 2, 3]
def add_stock_portfolio(self, layout):
layout.clear_widgets()
for i in self.portfolio_list:
layout.add_widget(StockPortfolio())
And the beginning of the custom widget kv:
<StockPortfolio#FloatLayout>
id: stockportfolio
size_hint: None, None
height: app.root.height * .18
width: app.root.width -10
canvas:
Color:
rgba: .98, .98, 0, .5
Rectangle:
size: self.size
pos: self.pos
Button:
background_normal: ''
background_down: ''
background_color: [0, 0, 0, 0]
pos: 5, 5
on_release:
app.go_screen(4)
app.load_popup2()
app.update_current(portfolioticker1.text, portfoliocompany.text)
BoxLayout:
orientation: "vertical"
pos: 5, 5
size_hint: None, None
height: app.root.height * .18
width: app.root.width -10
canvas:
Color:
rgba: .98, .98, .98, 1
Rectangle:
size: self.size
pos: self.pos

Problems
In your Kivy App, there are two different classes of StockPortfolio(). The one declared in the kv file is a Dynamic Class whereas the other one declared in Python script is a class Rule. Dynamic class does not require an implementation in Python script.
StockPortfolio objects added are stacked on top of each other.
Solution
In the kv file, change the Dynamic class to class rule by removing #FloatLayout.
Provide button's size
Provide new position for each stock portfolio added into layout.
Snippets - kv file
<StockPortfolio>:

Related

Kivy align Switch left in BoxLayout

I want to align a switch to the left side of a BoxLayout. For labels i achieved this with the following code:
text_size: self.size
This places my label text on the bottom-left corner of my boxlayout. However, i cant manage to do the same with a switch widget. I tried playing around with size_hint_x, size, pos and so on, but i cant align the elements properly without disturbing the sizes of the boxes.
Currently my labels are aligned properly, so i tried assigning ids to them and orientating the switch according to the current pos of the label with the following:
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
text_size: self.size
valign: 'middle'
Label:
text: 'this is already correctly aligned'
id: 'labelCorrectlyAligned'
#Some other code
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
#here i need something like text_size: self.size but for switches
Switch:
size_hint_x: labelCorrectlyAligned.pos[0] #this should be the current X-position of the label
#pos_hint_x: labelCorrectlyAligned.pos[0] #didnt work either
If I understand your question correctly, you can just set the size of the Switch to its minimum as noted in the documentation:
The minimum size required is 83x32 pixels
Any size bigger than that will just have the 83 by 32 image centered in that larger size.
Also, in these layout situations, I find it helpful to color the backgrounds of Widgets to easily see exactly where they are and how big they are. Here is a modified version of your 'kv` that does both of the above suggestions:
BoxLayout:
orientation: 'vertical'
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'this is already correctly aligned'
text_size: self.size
valign: 'middle'
id: 'labelCorrectlyAligned'
canvas.before:
Color:
rgba: 0,1,0,1
Rectangle:
pos: self.pos
size: self.size
# Some other code
BoxLayout:
padding: 100, 0, 0, 0
orientation: 'horizontal'
canvas.before:
Color:
rgba: 1,1,0,1
Rectangle:
pos: self.pos
size: self.size
Switch:
size_hint: None, None
size: 83, 32
on_active: app.setBluetoothConnection(self)
canvas.before:
Color:
rgba: 0,0,1,1
Rectangle:
pos: self.pos
size: self.size
Of course, when you are satisfied with the layout, just remove the canvas.before: blocks.
By the way, the text_size and valign have no effect in the BoxLayout, they must be in the Label rule.
So, both the Switch and the Label are on the left of the BoxLayout. A common way to size a Label is to use:
size_hint: None, None
size: self.texture_size
This gives you the minimum size for a Label.
Edit: For convenience, i created a dynamic class for my left-aligned switch:
<SwitchL#Switch>:
size_hint_x: None
size: 83, self.height

AttributeError: 'MyGrid' object has no attribute for my function

The Goal
I am trying to create a simple script that demonstrates how to attach a function to a button that can view (and soon change) properties of another widget.
I know this is possible based off of the example script effectwidget.py.
My Methods
At the moment, I'm trying to reverse engineer effectwidget.py because it does a lot of things I'll probably want to do with kivy.
In effectwidget.py, the SpinnerRow class has a function called update_effectwidget() that can view/edit attributes of specific instances of ComparisonWidget (which isn't even a child of SpinnerRow in the widget tree). SpinnerRow has child widgets EffectSpinner which trigger update_effectwidget().
In my script, ButtonHolder plays the role of SpinnerRow and colorChange1() plays the role of update_effect().
My Code
This code was reduced to only show the reproducible error. So I don't intend on using this to change the color of the labels.
#!/usr/bin/env python3
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang import Builder
class ButtonHolder(BoxLayout):
def colorChange1(self, *args):
print("this function works")
Builder.load_string("""
<MyGrid>:
rows: 3
Label:
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
id: toplabel
Label:
canvas.before:
Color:
rgba: 0,1,0,1
Rectangle:
pos: self.pos
size: self.size
id: bottomlabel
ButtonHolder:
Button:
effectwidget: toplabel
on_press: root.colorChange1()
""")
class MyGrid(GridLayout):
pass
class TheApp(App):
def build(self):
return MyGrid()
TheApp().run()
The Problem
I am receiving the following error:
AttributeError: 'MyGrid' object has no attribute 'colorChange1'
My questions to you
Why is my function colorChange1() inside my ButtonHolder not being found when it follows the same structure as effectwidget.py?
For the purposes of scope and management, it's not practical for me to give every class its own functions so they can be called by self.functionName(). And if root.functionName() only calls functions in the very root widget (rather than any parents widgets along the way), wouldn't that mean that the root widget of a large kivy program would have to contain dozens of functions?
Note:
The closest questions I could find to this were Kivy 'object has no attribute' Error
and AttributeError: 'Button' object has no attribute 'update_label'.But their cases were too complex and specific to find the answer to my general problem. But I have seen them.
Your main one is the ignorance of the concept of root. To observe it better I have indented better your code:
<MyGrid>:
rows: 3
Label:
id: toplabel
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
Label:
id: bottomlabel
canvas.before:
Color:
rgba: 0,1,0,1
Rectangle:
pos: self.pos
size: self.size
ButtonHolder:
Button:
effectwidget: toplabel
on_press: root.colorChange1()
The root is the initial element of the structure, in this case it is MyGrid.
Does MyGrid have a colorChange1 method? No, that's why you get that error.
What class does the colorChange1 method belong to? belongs to the ButtonHolder class, then the root must be changed by a reference of an object through an id.
<MyGrid>:
rows: 3
Label:
id: toplabel
canvas.before:
Color:
rgba: 1,0,0,1
Rectangle:
pos: self.pos
size: self.size
Label:
id: bottomlabel
canvas.before:
Color:
rgba: 0,1,0,1
Rectangle:
pos: self.pos
size: self.size
ButtonHolder:
id: button_holder # <---
Button:
effectwidget: toplabel
on_press: button_holder.colorChange1() # <---

kivy TextInput change font color add background lines

I am developping a simple Notes App. This is how my screen for adding a Note looks right now.
I would like to achieve two things:
1. Change the font color of my TextInput to white.
2. Add lines to my TextInput as seen in the picture
The lines should always be visible even if the TextInput is empty.
Extract from my python script:
class NewNoteView(ModalView):
pass
Extract from my kv file
<NewNoteView>:
BoxLayout:
orientation: 'vertical'
BoxLayout:
size_hint_y: 0.2
canvas.before:
Color:
rgb: 33/255, 41/255, 55/255
Rectangle:
pos: self.pos
size: self.size
orientation: 'vertical'
BoxLayout:
Button:
size_hint_x:0.1
text: 'x'
font_size: self.height*0.5
background_color: 0, 0, 0, 0
on_press: root.dismiss()
Label:
text: 'New Label'
Button:
size_hint_x:0.1
text:'+'
font_size: self.height*0.5
background_color: 0, 0, 0, 0
on_press: app.root.notifyP()
Button:
size_hint_x:0.1
text: 'L'
font_size: self.height*0.5
background_color: 0, 0, 0, 0
BoxLayout:
TextInput:
font_size: self.height*0.5
background_color: 0, 0, 0, 0
cursor_color: 1, 1, 1, 1
hint_text: 'Heading'
multiline: False
padding_x: [30,30]
Button:
size_hint_x: 0.2
BoxLayout:
canvas.before:
Color:
rgba: [38/255, 49/255, 70/255,1]
Rectangle:
pos: self.pos
size: self.size
TextInput:
background_color: 0, 0, 0, 0
cursor_color: 1, 1, 1, 1
color: 1, 1, 1, 1
hint_text: 'Body'
padding_x: [30,30]
Your question is very clearly structured (thanks!) but you didn't really do your research properly, did you? At least regarding the first question.
The answer is simple: use foreground_color attribute and set it to 1, 1, 1, 1; from the docs:
(fore_ground color is the) current color of the foreground, in (r, g,
b, a) format. Defaults to black.
Now this one is a bit more interesting and complicated. The straight-forward solution is to use the Line in your canvas. Something like this will add a white line to the bottom of the widget in the .kv file:
canvas.before:
Color:
rgba: 1, 1, 1, 1
Line:
points: self.x + 20, self.y, self.x + self.width - 20, self.y
width: 1
And it looks like this when I changed your app to use two TextInputs:
But, as I understand, you only want to have one TextInput in a note. So you have to figure out the line heights and draw a line every x pixels. Here's what I found. You need to either use minimum_height or line_height plus the line_spacing. Also, I don't think you can do this in .kv, I think you'll need to write a method for it in Python.
I would recommend using my method with extra TextInputs. You can bind "enter" to create a new TextInput every time so you can have infinite lines. I believe this will be easier, but you can do it either way.

How to center buttons in Kivy?

I am new to Python UI programming, and I am trying out Kivy. I want to center some buttons on my screen, but the buttons do not move from the bottom right of the window. I was wondering if anyone could point out what I am doing wrong or am missing?
vgx.kv:
#:kivy 1.9.1
<VgxMainScreen>:
BoxLayout:
size_hint: 0.2,0.2
size: 200, 100
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
<VgxUI>:
AnchorLayout:
anchor_x: 'center'
VgxMainScreen:
size_hint: 0.2,0.2
<VgxApp>:
FloatLayout:
VgxUI:
center: 0.5, 0.5
VgxApp.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
kivy.require('1.9.1')
class VgxMainScreen(Widget):
pass
class VgxUI(Widget):
pass
class VgxApp(App):
def build(self):
return VgxUI()
if __name__ == '__main__':
VgxApp().run()
Edit: Best way to debug what's going on with you widgets is to change their background colors :).
canvas.before:
Color:
rgba: X, X, X, 1
Rectangle:
pos: self.pos
size: self.size
The problem was that your BoxLayout wasn't positioned relatively to its parent even though it was inside it.
What I did here is that I positioned the BoxLayout to its parent's size and position using:
size: self.parent.size
pos: self.parent.pos
I also moved the size property from VgxMainScreen to VgxUI because I assume this is more common use-case (you can have multiple VgxMainScreens each with different size). Full code with background colors:
#:kivy 1.9.1
<VgxMainScreen>:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size # important!
pos: self.parent.pos # important!
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
<VgxUI>:
AnchorLayout:
canvas.before:
Color:
rgba: 1, 1, 1, 1 # white
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size
anchor_x: 'center'
anchor_y: 'center'
VgxMainScreen:
size: 200, 100
<VgxApp>:
FloatLayout:
VgxUI:
center: 0.5, 0.5
There's yet another solution to this.
You can use RelativeLayout and all widgets inside it will position relatively to this layout.
<VgxMainScreen>:
size_hint: None, None
RelativeLayout:
pos: self.parent.pos
size: self.parent.size
BoxLayout:
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
I've personally scratched my head many times when using Kivy and wondered why my widgets aren't positioned as I think they should :).
VgxMainScreen is a Widget, so it doesn't apply any positioning or sizing to its children (only Layouts do that), so the BoxLayout has the default position of (0, 0) and size of (100, 100).
Make VgxMainScreen inherit from something else, like a FloatLayout.

Kivy scrollview with the kivy language and a custom widget

I'm using Kivy, a Python Library, and i'm having trouble getting a scroll view to work, and organizing it into a .kv file.
I have a big system that's broken up into many .py and .kv files.
I am also using kivy's screen manager
ui_manager.py
#ui_manager.py
class UIManager():
_ScreenManager = None
def __init__(self, inScreenManager):
self.ScreenManager = inScreenManager # The main app constructs the manager, sends to me, then returns it as the root widget.
main.py
#main.py
class MyApp(App):
def build(self):
screenManager = ScreenManager()
uiManager = ui_manager.UIManager(screenManager)
return screenManager
Obviously I ommited a little code, but this is almost exactly what happens.
One of the screns needs to scroll. It will have a long graphic and many buttons.
The Kivy documentation, as usual, only tells me how to do it via code, NOT via the kivy language (.kv files)
http://kivy.org/docs/api-kivy.uix.scrollview.html
I want a class, OrangeWidget, to hold the scrolling widget, so I can play with its data later on.
Here is my best attempt at getting an organized scrollview working
orange_widget.py
#orange_widget.py
class OrangeWidget(Screen):
pass
class OrangeGraphic(Widget):
pass
Orange.kv
<OrangeLineWidget>
ScrollView:
size_hint: (None, None)
size: (400, 1200)
OrangeLineGraphic:
pos: root.pos
<OrangeGraphic>
canvas:
Color:
rgba: 1, .5, 0, 1
Rectangle:
pos: self.center_x - 15, 0
size: 30, self.height * 2
Label:
text: "Hello"
I know it's a lot, and it's not looking well organized already, but i'm just trying to figure out how to get the dang thing working properly.
Here is the current result: It won't scroll, and for whatever reason it's been "boxed" to the right, instead of taking up the full screen
as answered on the ML, here you don't define the Label pos, your OrangeGraphic being a simple widget, not a layout, you need to do it:
<OrangeGraphic>
canvas:
Color:
rgba: 1, .5, 0, 1
Rectangle:
pos: self.center_x - 15, 0
size: 30, self.height * 2
Label:
text: "Hello"
pos: root.pos
size: root.size
Looks like the trick, for this particular "sliding" screen, was to use a "Relative Layout"
https://groups.google.com/forum/#!topic/kivy-users/RwuI8QGm3fw
Here is the updated code:
orange_widget.py
class OrangeWidget(Screen):
def __init__(self, **kwargs):
super(OrangeWidget, self).__init__(**kwargs)
scrollView = ScrollView(size_hint=(1, 1))
# add custom widget into that layout
customWidget = OrangeGraphicWidget(height=1200, size_hint_y=None)
#layout.bind(minimum_height=layout.setter('height'))
scrollView.add_widget(customWidget)
self.add_widget(scrollView)
class OrangeGraphicWidget(RelativeLayout):
pass
OrangeWidget.kv
<OrangeWidget>
<OrangeGraphicWidget>
canvas:
Color:
rgba: 1, .5, 0, 1
Rectangle:
pos: self.center_x - 15, 20
size: 30, self.height - (self.height / 10)
Button:
text: "Button 1"
pos: root.pos
size_hint: (None, None)
Button:
text: "Button 2"
pos_hint: {'center_x': .5, 'center_y': .95}
size_hint: (None, None)

Categories

Resources