Kivy - Referencing IDs of Subclasses (Nested IDs) - python

I'm creating a kivy app right now. I'm quite new to Kivy and kv lang and there doesn't seam to be much rumour about it although i find it great to seperate code logic and layout, especially after some pygame development.
So to my actuall problem: I have a wiki-style screen for screenmanager, consisting out of a BoxLayout:
Title as Label
Scrollable Label for the Text (later , there shall be displayed a nested kv file)
Buttons for Navigation (scroll up and go back to main screen)
Now I'm recreating the Navigation buttons to be floating type as known from many webpages and apps. Problem is, I suddendly cant reference my ScrollView anymore. Any help or suggestions?
<Wiki>:
name: "wiki"
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: "picture.jpg"
BoxLayout:
id: box
orientation: "vertical"
padding: 10
spacing: 10
Label:
font_size: 20
size_hint_y: .1
text: root.country_text
ScrollView:
id: scrlvw
BackgroundLabel:
background_color: 220,220,220, 0.5
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
halign: "left"
valign: "top"
text: root.wiki_text
FloatLayout:
size_hint_y: .1
Button:
size_hint_x: .2
pos_hint: {"center_x": .25, "center_y": .5}
text: "Back"
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
FloatButton:
size_hint_x: .2
pos_hint: {"center_x": .75, "center_y": .5}
text: "Up"
on_release:
widget = BoxLayout()
widget.ids.scrlvw.scroll_y = 0
Before, when it worked, it was:
BoxLayout:
FloatLayout:
size_hint_y: .1
Button:
size_hint_x: .2
pos_hint: {"center_x": .25, "center_y": .5}
text: "Back"
on_release:
app.root.current = "main"
root.manager.transition.direction = "right"
Button:
size_hint_x: .2
pos_hint: {"center_x": .75, "center_y": .5}
text: "Up"
on_release:
scrlvw.scroll_y = 0
Well as its just a design question, I guess i temporary have to dismiss the floating design. But I would be so pleased if you could help me understand this language better.

As long as the 'kv' code described as "when it worked" is still in the same <Wiki> rule, it should still work. The newer kv code will never work as you are trying to create a new BoxLayout and reference an id in it. That has two problems:
The newly created BoxLayout is not the BoxLayout instance that appears in your GUI, so any changes to it will have no effect on what appears in the display.
Unless you have a <BoxLayout> rule in your kv, the newly created BoxLayout will not have a scrlvw id.
The ids defined within a kv rule are available for use only within that rule. See the documntation.

Related

Appending and removing code elements within Kivy language on button click

I have written a Kivy GUI that consists of various buttons and nested layouts. I want to be able to, on a click of a button, append a section of code n number of times within one of these layouts, specifically the scroll layout. Since I have am very new to Kivy and since I cannot seem to find a tutorial on this matter, I am presenting it here.
Here is the Python code:
import kivy
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.widget import Widget
class Interface(Widget):
pass
class GUI(App):
def build(self):
return Interface()
if __name__ == "__main__":
GUI().run()
And the total Kivy code:
<Interface>:
GridLayout:
padding:0
size: root.width, root.height
cols:1
#Top buttons
GridLayout:
size_hint: 1, 0.07
padding:0
cols:1
Button:
text:"Add Phase"
#Chart areas
ScrollView:
do_scroll_y:False
BoxLayout:
orientation: "vertical"
size_hint_x: None
width: self.minimum_width
GridLayout:
size_hint_x: None
width: self.minimum_width
cols:20
#Phase template
GridLayout:
width: 200
size_hint_x: None
cols:1
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Phase"
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Step"
Button:
size_hint:1, 0.07
text:"Add Step"
GridLayout:
cols:20
#Step template
GridLayout:
width: 100
size_hint_x: None
cols:1
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Var1"
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Var2"
Button:
background_normal: ''
background_color: 0.28,0.59,0.72,1
text:"Test"
Button:
size_hint:1, 0.07
text:"Delete"
Button:
background_color: 0.8,0,0,1
size_hint:1, 0.07
text:"Delete"
You will see in the Kivy code that these is a commented secion called #Phase template. Basically on pressing the button Add Phase, this entire section and its children elements should be appended in the immediate parent GridLayout.
Here you can press the Add Phase button:
Which will result in this:
And then finally, pressing the Delete button should remove that specific appended section of code.
Again, no idea how to approach this from the Kivy language, which seems a bit rigid to work with. But I am sure what I want to do can be accomplished.
One way to accomplish that is to create a Phase class, and add a kv rule for building instances of Phase. Then, in kv, you can use Factory.Phase() to create new instances.
Modify your kv as:
#:import Factory kivy.factory.Factory
<Interface>:
GridLayout:
padding:0
size: root.width, root.height
cols:1
#Top buttons
GridLayout:
size_hint: 1, 0.07
padding:0
cols:1
Button:
text:"Add Phase"
on_release: grid.add_widget(Factory.Phase()) # this adds another Phase
#Chart areas
ScrollView:
do_scroll_y:False
BoxLayout:
orientation: "vertical"
size_hint_x: None
width: self.minimum_width
GridLayout:
id: grid # added id to identify where to add new Phase instances
size_hint_x: None
width: self.minimum_width
cols:20
# initial Phase instance
Phase:
#Phase template
<Phase#GridLayout>:
width: 200
size_hint_x: None
cols:1
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Phase"
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Step"
Button:
size_hint:1, 0.07
text:"Add Step"
GridLayout:
cols:20
#Step template
GridLayout:
width: 100
size_hint_x: None
cols:1
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Var1"
TextInput:
size_hint: 1, 0.06
halign: 'center'
hint_text:"Var2"
Button:
background_normal: ''
background_color: 0.28,0.59,0.72,1
text:"Test"
Button:
size_hint:1, 0.07
text:"Delete"
Button:
background_color: 0.8,0,0,1
size_hint:1, 0.07
text:"Delete"
on_release: root.parent.remove_widget(root) # delete this Phase
Key points are the <Phase#GridLayout> rule, the new grid id, and the use of Factory in the Add Phase Button.

Trying to call a layout class within a screen layout - kivy

Essentially, I have multiple screens, being controlled by the screen manager (primarily for logins), however on the main screen, I have it broken up with a header, nav bar, and a section for the body. In the body section is where I want to be able to call different layouts that I have classes for, and add and remove these as buttons on the nav bar are pressed.
Im using kivy to control all aspects of the gui, but am rather new to it. At the moment I have:
Builder.load_string('''
<MainMenu>:
FloatLayout:
canvas.before:
Color:
rgba: 0.235, 0.271, 0.302, 1
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'vertical'
rows: 4
Label:
size_hint: 1,0.12
text: "ProductName"
text_size: self.size
halign: 'left'
valign: 'middle'
font_size: 32
color: [0.114,0.18,0.224,1]
StackLayout:
size_hint: 1,0.10
orientation: 'lr-tb'
#Purchase Order System
Button:
id: butpos
text: "Purchase Order"
size_hint: 0.166,1
on_press: spaceholder.add_widget(pos)
#Asset Assignment System
Button:
id: butaas
text: "Asset Assignment"
size_hint: 0.166,1
#Review and Revise System
Button:
id: butrrs
text: "Review"
size_hint: 0.166,1
#Administrative System
Button:
id: butadm
text: "Administration"
size_hint: 0.166,1
#Analytics and Reporting System
Button:
id: butars
text: "Analytics"
size_hint: 0.166,1
#Placeholder, possibly documentation
Button:
id: butbut
text: "Placeholder"
size_hint: 0.166,1
Label:
canvas.before:
Color:
rgba: 0.259, 0.643, 0.937, 1
Rectangle:
pos: self.pos
size: self.size
size_hint: 1,0.005
Widget:
id: spaceholder
<PurchaseOrder>
id: pos
orientation: 'lr-tb'
#Asset Details
Label:
BoxLayout:
orientation: 'vertical'
Label:
text: "Asset Requirements"
Label:
text: "Asset1: "
TextInput:
id: as1count
Label:
text: "Asset2: "
TextInput:
id: as2count
''')
class MainMenu(Screen):
pass
class PurchaseOrder(StackLayout):
pass
class MainApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainMenu(name='mainmenu'))
return sm
if __name__ == '__main__':
MainApp().run()
PurchaseOrder is a class that Id like to be added or removed, much like classes I intend to set up for the other navigation options. Ive tried a number of methods, but I think im missing something. Im either being thrown with 'pos' is not defined, or that 'spaceholder' has wrong attributes when ive tried different combinations.
You can make that work by modifying the Purchase Order Button:
on_press: spaceholder.add_widget(Factory.PurchaseOrder())
That requires an addition to your kv to import Factory:
#:import Factory kivy.factory.Factory
I would recommend using a Layout for your spaceholder to take advantage of their sizing and positioning capabilities. For example, using an AnchorLayout automatically centers its child (but is limited to a single child). Like this:
AnchorLayout:
id: spaceholder

kivy scrollview consisting of maplotlib plots not scrolling

I'm currently working on building a digital planner app, where I want to include some statistics-related features. Obviously, matplotlib is an optimal means of doing that, but I have two problems with it when I try to add more than one plot in a a Kivy ScrollView:
Each plot decreases in size so much that you cannot see what's
actually being displayed;
Kivy ScrollView is not scrolling - unfortunately, very common.
I've tried setting ScrollView's height equal to ScrollView.minimum_height and yet I get no result.
Here's a bit of my Python code:
class StatsWindow(FloatLayout, MDTabsBase):
dates_from, dates_to, plot_scroll = [str(datetime.today()).split()[0]], [str(datetime.today()).split()[0]], ObjectProperty(None)
def add_sp_plot(self, date_from, date_to):
# There were too many lines of data handling and plot creating, so I've only left the KivyPart:
# ------- KIVY part ------- KIVY part ------- KIVY part ------- KIVY part ------- #
self.plot_scroll.plot_layout.clear_widgets()
self.plot_scroll.plot_layout.add_widget(FigureCanvasKivyAgg(plt.gcf(), size_hint_y=None))
self.plot_scroll.plot_layout.add_widget(FigureCanvasKivyAgg(plt.gcf(), size_hint_y=None))
self.plot_scroll.plot_layout.add_widget(FigureCanvasKivyAgg(plt.gcf(), size_hint_y=None))
Here's a bit of my Kivy code:
<StatsWindow>:
name: "stats"
text: "STATS"
icon: "thumbs-up-down"
plot_scroll: plot_scroll
choose_date_to: choose_date_to
choose_date_from: choose_date_from
FloatLayout:
MDLabel:
halign: "center"
size_hint: 1, .1
text: "Choose the dates to view your stats"
font_size: self.width / 17
color: app.theme_cls.primary_color
pos_hint: {"top": .98, "center_x": .5}
BoxLayout:
MDFlatButton:
id: choose_date_from
pos_hint: {"center_x": .25, "top": .88}
text: "from"
size_hint: .4, .1
font_size: self.width / 11
on_release: root.open_date_picker(root.dates_from, self)
MDFlatButton:
text: "|"
size_hint: .1, .1
pos_hint: {"center_x": .5, "top": .88}
MDFlatButton:
id: choose_date_to
font_size: self.width / 11
pos_hint: {"center_x": .75, "top": .88}
text: "to"
size_hint: .4, .1
font_size: self.width / 11
on_release:
root.open_date_picker(root.dates_from, self)
BoxLayout:
MDFlatButton:
size_hint: 1, .1
text: "Load the statistics"
pos_hint: {"center_x": .5, "top": .78}
on_release: root.add_sp_plot(choose_date_from.text, choose_date_to.text)
ScrollView:
id: plot_scroll
do_scroll_y: True
do_scroll_x: False
pos_hint: {"top": .68}
plot_layout: plot_layout
size: root.width, root.height
GridLayout:
id: plot_layout
cols: 1
height: self.minimum_height
Here's the picture of what I get when I run the program:
The ScrollView will only scroll if its child (the GridLayout) is larger than the ScrollView. Also, the line:
height: self.minimum_height
will have no effect unless you add the line:
size_hint_y: None
You can increase the size of the plots by specifying a row_default_height for the GridLayout and eliminating the size_hint_y=None for the FigureCanvasKivyAgg. So, I would suggest specifying your ScrollView as:
ScrollView:
id: plot_scroll
do_scroll_y: True
do_scroll_x: False
pos_hint: {"top": .68}
plot_layout: plot_layout
GridLayout:
id: plot_layout
cols: 1
size_hint_y: None
row_default_height: 500 # any default row height that you desire
height: self.minimum_height
Then, add the plots using:
def add_sp_plot(self, date_from, date_to):
# There were too many lines of data handling and plot creating, so I've only left the KivyPart:
# ------- KIVY part ------- KIVY part ------- KIVY part ------- KIVY part ------- #
self.plot_scroll.plot_layout.clear_widgets()
self.plot_scroll.plot_layout.add_widget(FigureCanvasKivyAgg(plt.gcf()))
self.plot_scroll.plot_layout.add_widget(FigureCanvasKivyAgg(plt.gcf()))
self.plot_scroll.plot_layout.add_widget(FigureCanvasKivyAgg(plt.gcf()))
Removing the size_hint_y=None leaves the default value of size_hint_y as 1, so that the plot will take up all the space that the GridLayout allocates to it (the row_default_height).

Are CardTransition modes bugged?

I try to use a CardTransition in kivy. I want to make 'OtherScreen' slide from the right, and when you go back, it will slide back. I tried it with using 'push' and 'pop' as the mode
I tried using only 'push', which works, or only 'pop', which also works, but I can't seem to combine the two
'''
MainScreen:
name: 'main'
FloatLayout:
size: root.width, root.height
canvas.before:
Color:
rgba: 1, 1, 1, 1
Rectangle:
size: root.width, root.height
Button:
text: '2D'
size: root.height / 2.5, root.height / 2.5
size_hint: None, None
pos_hint: {'center_y': 0.75, 'center_x': 0.5}
on_press:
app.root.current = '2d'
root.manager.transition.direction = "left"
**root.manager.transition.mode = 'push'** *# this is push*
font_size: 50
background_normal: 'ok.png'
OtherScreen:
name: '2d'
FloatLayout:
size: root.width, root.height
Button:
on_press:
app.root.current = 'main'
root.manager.transition.direction = "right"
**root.manager.transition.mode = 'pop'** *# and this is pop*
text: 'back'
'''
After I click the first Button, 'OtherScreen' slides in, but after that the animations completely disappear
Root Cause
The CardTransition is not working as you expected because in your app, it switched the screen before setting the direction.
Snippets
on_press:
app.root.current = '2d'
root.manager.transition.direction = "left"
...
on_press:
app.root.current = 'main'
root.manager.transition.direction = "right"
Solution
Set the transition direction
Switch screen
Snippets
on_press:
root.manager.transition.direction = "left"
app.root.current = '2d'
...
on_press:
root.manager.transition.direction = "right"
app.root.current = 'main'

Why won't my kv BoxLayout centre horizontally?

This is my test.kv file:
BoxLayout:
BoxLayout:
orientation: 'vertical'
size_hint: None, None
height: '160sp'
width: '380sp'
pos_hint: {'center_x': .5, 'center_y': .5}
BoxLayout:
Label:
text: 'UserName'
TextInput:
id: user_name
text: ''
BoxLayout:
Label:
text: 'Password'
TextInput:
id: password
password: True
text: ''
BoxLayout:
Label:
text: 'Domain'
TextInput:
id: domain
text: 'howefarmingco.local'
Button:
text: 'Login'
size_hints: None, 1
width: .6
pos_hint: {'center_x': .5}
on_press: app.do_login()
The idea is to have the login fields appear centred both vertically and horizontally. The vertical works as I expect it to, but the fields and button all display on the left edge of the window instead of the middle.
Am I missing something very basic here or just going about it in entirely the wrong way?
pos_hint is not used by all layouts!
From FloatLayout docs: FloatLayout honors the pos_hint and the size_hint properties of its children.
Changed my test.kv file so outer BoxLayout is now a FloatLayout and everything works as expected.

Categories

Resources