flexible number of objects in kivy scrollview - python

I am trying to use input on one screen to make a list on a second screen using kivy. The basic idea is that a paragraph is broken up into words, which are then displayed on another page. Below is a minimum example.
What is happening is that the words are getting squished together, and there's no scrolling.
In main.py:
from kivy.app import App
from kivy.app import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
class InputScreen(Screen):
wordList = ObjectProperty()
def getwords(self):
self.wordList=[]
s = self.ids.textInput.text.split()
for w in s:
for punctuation in [',','.','!','?']:
w.strip(punctuation)
self.wordList.append(w)
return None
class WordLabel(Label):
pass
class WordScreen(Screen):
wordList = ObjectProperty()
def updateWords(self):
self.ids.scrollContainer.clear_widgets()
wordGrid = GridLayout(cols=2,size_hint_y=None)
wordGrid.bind(minimum_height=wordGrid.setter('height'))
for w in self.wordList:
wordGrid.add_widget(Label(text=w))
wordGrid.add_widget(Label(text='property of word'))
self.ids.scrollContainer.add_widget(wordGrid)
class testScreenManager(ScreenManager):
pass
class testApp(App):
def build(self):
sm = testScreenManager()
return sm
if __name__=='__main__':
testApp().run()
In test.kv:
<testScreenManager>
InputScreen:
id: inputScreen
name: 'input'
WordScreen:
id: wordScreen
name: 'word'
wordList: inputScreen.wordList
<InputScreen>:
GridLayout:
cols: 1
TextInput:
id: textInput
hint_text: 'Input paragraph'
BoxLayout:
orientation: 'horizontal'
size_hint_y: .2
Button:
text: 'Analyze paragraph'
on_press: root.getwords()
Button:
text: 'See word list'
on_press:
root.getwords()
root.manager.transition.direction = 'left'
root.manager.current = 'word'
BoxLayout:
orientation: 'horizontal'
size_hint_y: .2
Label:
id: wordCountResult
text: ''
<WordScreen>:
on_enter: root.updateWords()
BoxLayout:
orientation: 'vertical'
BoxLayout:
id: rowLabels
orientation: 'horizontal'
size_hint_y: .2
Label:
text: 'Words not at grade level:'
size_hint_x: .5
Label:
text: 'Grade Level'
size_hint_x: .5
ScrollView:
id: scrollContainer
size_hint: (None,None)
size: (self.parent.width,self.parent.height - rowLabels.height - buttonRow.height)
Button:
id: buttonRow
size_hint_y: .2
text: 'Back'
on_press:
root.manager.transition.direction = 'right'
root.manager.current = 'input'
<WordLabel>:
size_hint_y: None
height: '29sp'
I've tried using size_hint_y=.6 for the ScrollView, and swapping height and minimum_height in the wordGrid.bind(minimum_height=wordGrid.setter('height')).
What I thought was going to happen, is that the ScrollView container will be the height of the window minus the height used by two other widgets. Inside the ScrollView, WordGrid is longer (the height is bound to the minimum height necessary for all children) and so the scrolling is used to see all the items. I must not be understanding some fact about heights/sizes of ScrollView, WordGrid, and WordGrid's children. Anyone have some insight?

In this part of the code:
for w in self.wordList:
wordGrid.add_widget(Label(text=w))
wordGrid.add_widget(Label(text='property of word'))
the Label by default will have size_hint_y=1. Instead, size_hint_y=None is necessary. A height can be set, as is done in the WordLabel class - which was what the OP (by me) meant to do.
Thanks to related question Kivy ScrollView - Not Scrolling for helping me to realize the error.

Related

Kivy scrollview with screenmanager and gridlayout

Like many other questions, im having issues with scrollview.
I'm trying to have a bunch of different buttons, that goes to different screens, but the buttons sit on top of each other, are stuck in the corner or only one button is appearing and the screen is cut in half.
I've tried having the screenmanager outside the TabbedPanelItem, giving every widget a size and no size_hint_y but to no avail? is it not possible to have the screenmanager inside the tabbedpanel or is it bad practice
Python file.
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.config import Config
from kivy.lang import Builder
Config.set('graphics', 'resizeable', 0)
Window.size = (375,700)
class MainScreen(Screen):
pass
class FirstScreen(Screen):
pass
class SecondScreen(Screen):
pass
root = Builder.load_file("main.kv")
class myapp(App):
def build(self):
return root
if __name__ == '__main__':
myapp().run()
main.kv
TabbedPanel:
do_default_tab: False
TabbedPanelItem:
text:"1"
ScrollView:
size: self.size
FloatLayout: #gridlayout? #boxlayout?
size_hint_y: None
ScreenManager:
id: manager
MainScreen:
name: 'main'
GridLayout:
spacing: 10
padding: 10
cols:1
row_default_height: 100
row_force_default: True
Button:
text: 'first'
on_press: manager.current = 'first'
size_hint: 1, None
height: 100
Button:
text: 'Second'
on_press: manager.current = 'second'
FirstScreen:
name: 'first'
SecondScreen:
name: "second"
TabbedPanelItem:
text:"2"
TabbedPanelItem:
text:"3"
<FirstScreen>:
BoxLayout:
Label:
text:"first"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
<SecondScreen>
BoxLayout:
Label:
text:"second"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
You can do what you want, but it seems a bit complicated. Here is a kv that I think will do it:
TabbedPanel:
do_default_tab: False
TabbedPanelItem:
text:"1"
ScrollView:
ScreenManager:
id: manager
size_hint_y: None
height: self.current_screen.height # only seems to work for first Screen
on_current_screen: self.height = self.current_screen.height # required because above line failure
MainScreen:
name: 'main'
size_hint_y: None
height: grid.height # set this Screen height to the height of the GridLayout
GridLayout:
id: grid
size_hint_y: None
height: self.minimum_height # set this height to the minimum required
spacing: 10
padding: 10
cols:1
row_default_height: 100
row_force_default: True
Button:
text: 'first'
on_press: manager.current = 'first'
size_hint: 1, None
height: 100
Button:
text: 'Second'
on_press: manager.current = 'second'
FirstScreen:
name: 'first'
SecondScreen:
name: "second"
TabbedPanelItem:
text:"2"
TabbedPanelItem:
text:"3"
<FirstScreen>:
size_hint_y: None
height: 100 # must set its height
BoxLayout:
Label:
text:"first"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
<SecondScreen>
size_hint_y: None
height: 100 # must set its height
BoxLayout:
Label:
text:"second"
Button:
text: 'Back to main'
on_press: root.manager.current = 'main'
A few things to note:
You don't need a Layout to contain the ScreenManager.
You need to set the height of the ScreenManager based on the height of its current Screen.
The line height: self.current_screen.height should adjust the height of the ScreenManager every time the current Screen changes, but it doesn't. So I added on_current_screen: self.height = self.current_screen.height to adjust the size when the current Screen changes.
Since the ScreenManager height depends on the Screen height, every Screen must have a well defined height.
The MainScreen height is determined by the GridLayout height, and the GridLayout height is calculated using self.minimum_height.

Kivy ScrollView in ScreenManager

I am trying to create several scrollable main screens that are connected with ScreenManager
However, when I try, I get the following error:
Only one root object is allowed by .kv
This happens when I add the WindowsManager in the kv file.
Can anyone advice me with how to resolve this?
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.uix.screenmanager import ScreenManager, Screen
class WindowManager(ScreenManager):
pass
class MenuPage(Screen):
pass
class MainPage(Screen):
pass
class Sections(BoxLayout):
label_text = StringProperty()
kv = Builder.load_file('main text')
class Scrollable(App):
def build(self):
return kv
def on_start(self):
self.root.ids.sv_box.add_widget(Sections(label_text=''))
Scrollable().run()
**kv file**
WindowsManager:
MainPage:
MenuPage:
BoxLayout:
orientation: 'vertical'
ScrollView:
do_scroll_y: True
BoxLayout:
orientation: 'vertical'
id: sv_box
size_hint_y: None
height: self.minimum_height
<Sections>:
orientation: 'vertical'
size_hint_y: None
height: 800
BoxLayout:
size_hint: (1,.5)
Button:
text: 'Menu'
size_hint: (.3,1)
BoxLayout:
Label:
text: 'Time'
```
According to kivy documentation a .kv file must contain only one root widget at most. In other words you can not have more than one class in the left most indentation level (except for dynamic classes) in kvlang.
In your posted code of .kv file there are two classes (WindowsManager and BoxLayout) at left(most) with same indentation level.
As from your code it's not clear which one of these two is the root widget, I assumed WindowsManager to be the root and moved the BoxLayout stuff under MainPage. Thus your modified main text.kv file now looks like,
WindowManager: # WindowsManager
MainPage:
BoxLayout:
orientation: 'vertical'
ScrollView:
do_scroll_y: True
BoxLayout:
orientation: 'vertical'
id: sv_box
size_hint_y: None
height: self.minimum_height
MenuPage:
<Sections>:
orientation: 'vertical'
size_hint_y: None
height: 800
BoxLayout:
size_hint: (1,.5)
Button:
text: 'Menu'
size_hint: (.3,1)
BoxLayout:
Label:
text: 'Time'

How to show languange specific characters on Kivy like Ä?

Im getting empty box character instead of Ä character. How to make them show on my kivy application. I read some posts suggesting changing font type to some which support these characters. i tried few but it didnt seem solving it.
Tried pretty basic Calibri font and also just randomly picked font which was said to be suitable with my languange( AlegreyaSansSC)
Python file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import StringProperty, BooleanProperty
class Row(BoxLayout):
x1 = StringProperty('')
x2 = StringProperty('')
x3 = BooleanProperty(False)
def __init__(self, x1, x2, x3, **kwargs):
super(Row, self).__init__(**kwargs)
self.x1 = x1
self.x2 = x2
self.x3 = x3
class MyPopup(Popup):
pass
class MainScreen(Screen):
pass
class SecondScreen(Screen):
def fire_popup(self):
pops = MyPopup()
pops.open()
class ScreenManagement(ScreenManager):
def changescreen(self, value):
try:
if value !='main':
self.current = value
except:
print('No Screen named'+ value)
class testiApp(App):
def build(self):
self.title = 'Hello'
def add_more(self, x1, x2, x3):
addbutton = self.root.get_screen('Page2').ids.empty
addbutton.add_widget(Row(x1, x2, x3))
def remove(self):
container = self.root.get_screen('Page2').ids.empty
if len(container.children) > 0:
container.remove_widget(container.children[0])
testiApp().run()
KV. file:
<MyPopup>:
id:pop
size_hint: .4, .4
auto_dismiss: False
title: 'XXX!!'
BoxLayout:
orientation:'vertical'
BoxLayout:
orientation:'horizontal'
Label:
text:'X1'
TextInput:
id: X1
Label:
text:'X2'
TextInput:
id:X2
CheckBox:
id:X3
BoxLayout:
orientation:'horizontal'
Button:
text:'Lisää'
on_release: app.add_more(X1.text, X2.text, X3.active)
Button:
text: 'Close'
on_press: pop.dismiss()
<Row>:
x1:''
x2:''
x3:False
Label:
text: root.x1
Label:
text: root.x2
CheckBox:
active: root.x3
ScreenManagement:
MainScreen:
name:'Main'
SecondScreen:
name:'Page2'
<MainScreen>:
name:'Main'
BoxLayout:
Button:
text:'Next Page'
on_release: app.root.current ='Page2'
<SecondScreen>:
name:'Page2'
BoxLayout:
orientation:'vertical'
BoxLayout:
orientation:'vertical'
Label:
text:'Popup Test'
ScrollView:
bar_width: 5
bar_color: 1,0,0,1 #red
bar_inactive_color: 0,0,1,1 #blue
effect_cls: 'ScrollEffect'
scroll_type:['bars','content']
GridLayout:
orientation: "vertical"
size_hint_y: None
height: self.minimum_height
row_default_height: 60
cols:1
id:empty
BoxLayout:
Button:
text:'Open Popup'
on_press: root.fire_popup()
Button:
text:'Add'
on_release: app.add_more()
Button:
text:'remove'
on_release: app.remove()
Question
Seems that problem happens only if i use separate py. and kv file.
Answer
I don't think the problem is caused by separate py and kv file. The following example displayed the character correctly.
main.py
from kivy.base import runTouchApp
from kivy.lang import Builder
runTouchApp(Builder.load_file("main.kv"))
main.kv
GridLayout:
cols: 2
Label:
text: 'Alegreya Sans SC font'
Label:
text: 'Roboto font (Default)'
Label:
text: 'Ä'
font_size: 56
font_name: '/home/iam/share/fonts/Alegreya_Sans_SC/AlegreyaSansSC-Medium.ttf'
Label:
text: 'Ä'
font_size: 56
Problem
I'm getting empty box character instead of Ä character. How to make
them show on my kivy application.
Solution
Download and extract Alegreya Sans SC font from Google Fonts. This font is under Open Font License
Add attribute, font_name: and provide the path to the extracted folder plus the type of font (e.g. AlegreyaSansSC-Medium.ttf).
Example
The following example illustrates displaying character, Ä using attribute, font_name to use different font. The default font use by Kivy is Roboto.
main-kv.py
from kivy.base import runTouchApp
from kivy.lang import Builder
runTouchApp(Builder.load_string("""
GridLayout:
cols: 2
Label:
text: 'Alegreya Sans SC font'
Label:
text: 'Roboto font (Default)'
Label:
text: 'Ä'
font_size: 56
font_name: '/home/iam/share/fonts/Alegreya_Sans_SC/AlegreyaSansSC-Medium.ttf'
Label:
text: 'Ä'
font_size: 56
"""))
Output

kivy multiscreen can not find the id

all, i can not pass the goi into the function submit_goi in class VQCIA in my kivy code. i'd appreciate if some one can fix the code. print self.goi.text() sentence reports a problem, and it says AttributeError: 'NoneType' object has no attribute 'text'. that is really weird.
# ---------- vqcia.py ----------
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<HelloVQCIA>:
BoxLayout:
Button:
text: "Welcome to VQCIA"
on_press:
# You can define the duration of the change
# and the direction of the slide
root.manager.transition.direction = 'left'
root.manager.transition.duration = 1
root.manager.current = 'screen_two'
<VQCIA>:
BoxLayout:
orientation: "vertical"
goi: g_o_i
padding: 10
spacing: 10
size: 400, 200
pos: 200, 200
size_hint:None,None
BoxLayout:
Label:
text: "Enter gene of interest with TAIR ID:"
font_size: '25sp'
BoxLayout:
TextInput:
id: g_o_i
hint_text: 'AT3G20770'
multiline: False
font_size: '25sp'
BoxLayout:
Button:
text: "Submit"
size_hint_x: 15
on_press:
root.submit_goi()
""")
class HelloVQCIA(Screen):
pass
class VQCIA(Screen):
# Connects the value in the TextInput widget to these
# fields
goi = ObjectProperty()
def submit_goi(self):
print self.goi.text
# The ScreenManager controls moving between screens
screen_manager = ScreenManager()
# Add the screens to the manager and then supply a name
# that is used to switch screens
screen_manager.add_widget(HelloVQCIA(name="screen_one"))
screen_manager.add_widget(VQCIA(name="screen_two"))
class VQCIAApp(App):
def build(self):
return screen_manager
dbApp = VQCIAApp()
dbApp.run()
I want to make a multiscreen app and the first page is a welcome page. and the second page is the main page that user can submit a form. thank you.
In kv file, move goi: g_o_i to after class rule, <VQCIA>:
<VQCIA>:
goi: g_o_i
BoxLayout:
orientation: "vertical"

how to enable/disable editing in TextInput using kivy in python

I have a piece of code. (1) The TextInput value should be shown , but first it should not be editable, after clicking the corresponding CheckBox, the TextInput will be editable.
(2) Using the iteration, the Label and the TextInput should get the value. The value at Label and TextInput should not be hard coded(although it's there in my code, #FJSevilla helped me for this one).
(3) However, the values of Label and TextInput are stored in a variable in json format. something like this(you can consider like key,value pair in map) [ variable = '{"a" : " Goc" , "b" : "Coc", "c" : "Dow" } ']
(you can see diagram for more clearance).
I appreciate the help.
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.lang import Builder
Builder.load_string("""
<Test>:
do_default_tab: False
TabbedPanelItem:
text: 'page1'
BoxLayout:
padding: 50, 50, 50, 50
orientation: 'horizontal'
BoxLayout:
spacing: 50
orientation: 'vertical'
size_hint_x: 1
Label:
text: 'a'
Label:
text: 'b'
Label:
text: 'c'
BoxLayout:
spacing: 50
orientation: 'vertical'
TextInput:
text: 'Goc'
TextInput:
text: 'Coc'
TextInput:
text: 'Dow'
BoxLayout:
spacing: 50
orientation: 'vertical'
size_hint_x: 0.40
CheckBox:
text: 'CheckBox'
CheckBox:
text: 'CheckBox'
CheckBox:
text: 'CheckBox'
BoxLayout:
spacing: 50
orientation: 'vertical'
size_hint_x: 0.60
Button:
text: 'save'
Button:
text: 'save'
Button:
text: 'save'
""")
class Test(TabbedPanel):
pass
class MyApp(App):
def build(self):
test = Test()
return test
if __name__ == '__main__':
MyApp().run()
First of all, thank you for providing an app which was easy to work with.
I tried to implement what you were looking for except the JSON. I am using a simple list, it should be straightforward to extend my code for a JSON.
Instead of using colums, I am using rows which makes it easier to link together the properties of the label textinput and checkbox.
from kivy.app import App
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty
from kivy.uix.textinput import TextInput
from kivy.uix.checkbox import CheckBox
from kivy.lang import Builder
ROWS = ['Goc', 'COC', 'EEE']
Builder.load_string("""
<Test>:
do_default_tab: False
TabbedPanelItem:
text: 'page1'
Table:
padding: 50, 50, 50, 50
orientation: 'vertical'
<Row>:
spacing: 50
#orientation: 'vertical'
size_hint_x: 1
txt: txtinpt.text
Label:
text: root.txt
TextInput:
id: txtinpt
text: root.txt
disabled: not CheckBox.active
CheckBox:
id:CheckBox
text: 'CheckBox'
active: False
Button:
text: 'save'
""")
class Table(BoxLayout):
def __init__(self, **kwargs):
super(Table, self).__init__(**kwargs)
for row in ROWS:
self.add_widget(Row(row))
class Row(BoxLayout):
txt = StringProperty()
def __init__(self, row, **kwargs):
super(Row, self).__init__(**kwargs)
self.txt = row
class Test(TabbedPanel):
pass
class MyApp(App):
def build(self):
test = Test()
return test
if __name__ == '__main__':
MyApp().run()

Categories

Resources