I have started building a simple app using Kivy (and am in the process of moving over to KivyMD for aesthetic purposes) and have come across an issue where all of the elements I'm rendering to screen are rendering twice: The first time they are static and uninteractable and the second time are the interactable ones over the top. Im working with some ScrollViews with Buttons in them and when I scroll the buttons underneath are visible. There is also Labels with text that I update, these still show the default text underneath them.
screenshot of double rendering elements
main.py
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.scrollview import ScrollView
from kivy.uix.togglebutton import ToggleButton
from kivy.properties import NumericProperty, ObjectProperty
class MagnateCalcApp(App):
def build(self):
return Builder.load_file("magnatecalc.kv")
class CalcWindow(Screen):
current_value = NumericProperty(100)
current_price = NumericProperty(1)
pricelabel = ObjectProperty(None)
valuelabel = ObjectProperty(None)
money = ObjectProperty(None)
def update(self):
if self.current_price != 1:
val = float(self.current_price)/1000
self.pricelabel.text = "Price: " + str(val) + "M"
if self.current_value != 100:
self.valuelabel.text = "Multiplier: " + str(self.current_value)
if self.current_price != 1 and self.current_value != 100:
val = (float(self.current_value) * float(self.current_price))/1000
print(val)
self.money.text = "Value: " + str(val) + "M"
class RentWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
if __name__ == "__main__":
MagnateCalcApp().run()
magnatecalc.kv (usually has quite a lot of buttons in the ScrollView but the majority of these have been removed for readability)
WindowManager:
CalcWindow:
RentWindow:
<Button>:
font_size:35
size_hint:0.5,0.5
<CalcWindow>:
name: "Calc"
current_value: 100
current_price: 1
pricelabel: pricelabel
valuelabel: valuelabel
money: money
GridLayout:
cols:2
size: root.width, root.height
Label:
text:""
pos_hint:{"x":0, "top":1}
size_hint_y:0.15
size_hint_x:0.05
ScrollView:
do_scroll_y:False
do_scroll_x:True
pos_hint:{"x":1, "top":1}
size_hint_y:0.15
GridLayout: # here i want a scrollview
id: price
rows: 1
size_hint_x: None
width: self.minimum_width
ToggleButton:
text:"300K"
size_hint_x: None
on_press:
root.current_price = 300
root.update()
group:"price"
ToggleButton:
text:"400K"
size_hint_x: None
on_press:
root.current_price = 400
root.update()
group:"price"
ToggleButton:
text:"500K"
size_hint_x: None
on_press:
root.current_price = 500
root.update()
group:"price"
ToggleButton:
text:"600K"
size_hint_x: None
on_press:
root.current_price = 600
root.update()
group:"price"
ToggleButton:
text:"700K"
size_hint_x: None
on_press:
root.current_price = 700
root.update()
group:"price"
ToggleButton:
text:"800K"
size_hint_x: None
on_press:
root.current_price = 800
root.update()
group:"price"
ScrollView:
do_scroll_y:True
do_scroll_x:False
pos_hint:{"x":0, "top":0}
size_hint_x:0.075
GridLayout: # here i want a scrollview
id: multiplier
cols: 1
size_hint_y: None
height: self.minimum_height
ToggleButton:
text:"2"
size_hint_y: None
on_press:
root.current_value = 2
root.update()
group:"multiplier"
ToggleButton:
text:"3"
size_hint_y: None
on_press:
root.current_value = 3
root.update()
group:"multiplier"
ToggleButton:
text:"4"
size_hint_y: None
on_press:
root.current_value = 4
root.update()
group:"multiplier"
ToggleButton:
text:"5"
size_hint_y: None
on_press:
root.current_value = 5
root.update()
group:"multiplier"
ToggleButton:
text:"6"
size_hint_y: None
on_press:
root.current_value = 6
root.update()
group:"multiplier"
ToggleButton:
text:"7"
size_hint_y: None
on_press:
root.current_value = 7
root.update()
group:"multiplier"
GridLayout:
rows:2
pos_hint:{"x":1, "top":0}
GridLayout:
cols:2
Label:
id:pricelabel
text:"Price: "
font_size:50
Label:
id:valuelabel
text:"Multiplier: "
font_size:50
GridLayout:
cols:2
Label:
id:money
text:"Value: "
font_size:90
size_hint_x:1
pos_hint:{"x":0, "top":0}
Button:
text:"<- Rent"
pos_hint:{"x":1, "top":0}
size_hint: 0.25,0.25
on_release:
app.root.current = "Rent"
root.manager.transition.direction="right"
<RentWindow>:
name: "Rent"
Button:
text:"Go Back ->"
on_release:
app.root.current="Calc"
root.manager.transition.direction="left"
Is there anything obvious that I am missing in my .py that would cause this? This issue appeared when I started switching over to KivyMD but I have since reverted those changes to try and get back to the original functionality and I can't so have clearly broken something along the way.
Your kv file is being loaded twice, once explicitly by you with Builder.load_file and once automatically because it has the same name as your app class.
The simplest solution is to skip the manual load and just delete your build method.
I had the same problem as you, and I figured out how to build correctly the app to stop duplicate the items.
In kivymd this is the correct way to build the app:
class MagnateCalcApp(MDApp):
def build(self):
return super().build()
I don't know how but it recognises the correct kivy file.
I had this same thing occur after I was using pyinstaller to create a distribution. The build and dist directories created by the pyinstaller process contained copies of my .kv files. This was causing all the visual elements to appear multiple times depending on how many copies of the files were present in other directories.
Moving or deleting these directories solved the issue for me.
Related
I currently have a code that runs a dropdown button in kivy. I would like to send the value 1,2,3,4 into the main class when the value is selected.
Specifying chain of events:
click dropdown button
four options are displayed
select one of the four options ==> this is when the value gets sent back to the main class
dropdown button text replaced with the selection
I have tried using get_screen(), which is equivalent to get_running_app when using windows, but this was delayed. I also tried creating an external variable but this was delayed too.
Below is my code. Thanks.
'''
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.dropdown import DropDown
class WindowManager(ScreenManager):
pass
class Drop_down(DropDown):
pass
class MenuWindow(Screen):
General_select_button_size = (100, 100)
General_select_button_pos = (400, 400)
class GeneralWindow(Screen):
drop_size = (100,100)
drop_pos = (400,400)
xax=''
def on_touch_up(self,touch):
app = self.manager.get_screen('General')
print('use get',app.ids['btn'].text)
print('use variable',self.xax)
KV = Builder.load_string("""
WindowManager:
MenuWindow:
GeneralWindow:
<MenuWIndow>:
name: 'Menu'
RelativeLayout:
Button:
allow_stretch: True
keep_ratio: True
size_hint: None,None
id: general_select_button
text: 'move to general mode'
size: root.General_select_button_size
pos: root.General_select_button_pos
on_release:
app.root.current = "General"
root.manager.transition.direction = "left"
<GeneralWindow>:
name: 'General'
RelativeLayout:
Button:
allow_stretch: True
keep_ratio: True
size_hint: None,None
id: btn
text: 'select number'
size: root.drop_size
pos: root.drop_pos
on_parent: drop_content.dismiss()
on_release:
drop_content.open(self)
root.xax=btn.text
DropDown:
id: drop_content
on_select: btn.text = '{}'.format(args[1])
Button:
id: btn1
text: '1'
size_hint_y: None
height: 30
on_release: drop_content.select('1')
Button:
id: btn2
text:'2'
size_hint_y: None
height: 30
on_release: drop_content.select('2')
Button:
id: btn3
text: '3'
size_hint_y: None
height: 30
on_release: drop_content.select('3')
Button:
id: btn4
text: '4'
size_hint_y: None
height: 30
on_release: drop_content.select('4')
""")
class ImageChangeApp(App):
def build(self):
return KV
if __name__ == '__main__':
ImageChangeApp().run()
'''
I have built a user interface with Kivy that appears correctly when the program starts up, but if I plug in a new monitor (or drag to an extended monitor), everything disappears under the window, but I can see part of my work when I maximize the window. How do I position my program to the start of the window?
I have tried to match the main layout top value to the window top value every second, and I have tried to reset the position of the main layout every second.
My kivy file
#:import inch kivy.metrics.inch
<Questionaire>:
#column_gl: ColumnGL
outer_column_gl: OuterColumnGL
cols: 2
GridLayout:
id: OuterColumnGL
cols: 1
size_hint_x: None
width: inch(2)
GridLayout:
id: ColumnGL
cols: 2
size_hint_y: .9
Button:
size_hint_x: None
width: inch(.1)
Button:
text: 'stuff'
Button:
size_hint_y: .1
text: 'more stuff'
My attempt to fix it
def fix_screen(*args):
self.y = 0
Clock.schedule_interval(fix_screen, .5)
This is what comes up when I first open the program.
This is what comes up as soon as I drag my window to an extended monitor (Everything appears to have disappeared).
Here is my maximized window on the extended monitor (The y position is wrong).
Using size_hint_x or size_hint_x allows you to set the size of your widgets relative to their parent widgets. Using ''pos_hint: {'x': val, 'y': val}'' will set the position of the widget to their parent widgets. Since size_hint, and pos_hint both are relative to their parent widgets, the values for the x and y are in a range from 0-1.
I would recommend utilizing size_hint_x: 0-1 rather than width for your buttons so they will automatically adjust to a changing resolution. I will come up with a sample to help you out.
Sizing and position are best done in the kv language whether you use builder or a .kv file. The added class and function are not needed to have reactive widgets. Let me know if you need more clarification or have any other questions about kivy.
Again I would recommend this following your widgets if you would like them to autoscale:
size_hint_x: 0.00-1.00
size_hint_y: 0.00-1.00
pos_hint: {'x': 0.00-1.00, 'y': 0.00-1.00}
An example that should show you how it works, although I doubt you need all the extra imports as this was to show consistency when changing resolutions:
from kivy.app import App
from kivy.lang import Builder
from kivy.config import Config
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
Config.set('input', 'mouse', 'mouse,multitouch_on_demand') #Prevent red dots from populating the screen with right clicks
Config.set('graphics','resizable',0) #Self-explanatory, prevents you from resizing the window
Config.write() #Writes the Config settings for the application
Builder.load_string("""
<One>:
GridLayout:
cols: 2
size_hint_x: .5
pos_hint: {'x': 0, 'y': 0}
GridLayout:
id: OuterColumnGL
cols: 1
size_hint_x: .2
GridLayout:
id: ColumnGL
cols: 2
size_hint_y: .9
Button:
size_hint_x: .3
Button:
text: 'stuff'
Button:
size_hint_y: .1
text: 'more stuff'
Button:
text: 'Change Res'
size_hint_x:.2
size_hint_y: .2
pos_hint: {'x': .75, 'y': .05}
on_release: root.enlarge()
<Two>:
GridLayout:
cols: 2
size_hint_x: .5
pos_hint: {'x': 0, 'y': 0}
GridLayout:
id: OuterColumnGL
cols: 1
size_hint_x: .2
GridLayout:
id: ColumnGL
cols: 2
size_hint_y: .9
Button:
size_hint_x: .3
Button:
text: 'stuff'
Button:
size_hint_y: .1
text: 'more stuff'
Button:
text: 'Change Res'
size_hint_x:.2
size_hint_y: .2
pos_hint: {'x': .75, 'y': .05}
on_release: root.shrink()
""")
class One(Screen):
def enlarge(self):
Window.size = (500, 600)
sm.current = 'two'
class Two(Screen):
def shrink(self):
Window.size = (300, 400)
sm.current = 'one'
sm = ScreenManager()
sm.add_widget(One(name='one'))
sm.add_widget(Two(name='two'))
class SampleApp(App):
def build(self):
Window.size = (300, 400)
Window.left = 750
Window.top = 40
return sm
if __name__ == '__main__':
SampleApp().run()
I am currently working on creating a pretty basic fitness app in Python using Kivy. It's been going pretty smoothly but I recently ran into a problem while trying to implement a dropdown menu into one of my screens. I am trying to put 3 different dropdown menus into one screen which will return certain values that I will later use for the main function of the app (which will be to generate a daily fitness routine). The problem is, each dropdown menu has the same option. For example, two of the dropdown menus I want to use are 'time availability'(30 mins, 60 mins...120 mins) and 'Fitness Level' (scale of 1-3). But each dropdown menu ends up having the same contents as whichever one I could first (such as time availability 30 mins, 60mins...120 mins and then the same for the contents inside of fitness level.)
Does anyone have any suggestions for how I can keep each dropdown menu unique within the same screen? The code for my 2 files (one .py and one .kv) are attached. In the code I have below, I removed the contents of the dropdown menus so they are basically empty buttons.
.py file:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.base import runTouchApp
import webbrowser
from kivy.core.window import Window
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
######################################################################
class KivyTutorRoot(BoxLayout):
def __init__(self, **kwargs):
super(KivyTutorRoot, self).__init__(**kwargs)
#list of previous screens
self.screen_list = []
def changeScreen(self, next_screen):
operations = "Get Fit, Create User".split(',')
question = None
#if screen is not already in the list of previous screens...
if self.ids.kivy_screen_manager.current not in self.screen_list:
self.screen_list.append(self.ids.kivy_screen_manager.current)
if next_screen == 'about this app':
self.ids.kivy_screen_manager.current = "about_screen"
elif next_screen == 'get fit':
self.ids.kivy_screen_manager.current = "getFitScreen"
def onBackBtn(self):
#check if there are any screens to go back to
if self.screen_list:
#if there are screens we can go back to. Then go back to that screen
self.ids.kivy_screen_manager.current = self.screen_list.pop()
#the pop() will return the last item from the list, aka the last screen we visited
#say we don't want to close
return True
#no more screens to go back to, so we close
return False
###############################################################################
#dropdown menu classes here:
class CustomDropDownTime(DropDown):
pass
class CustomDropDownGym(DropDown):
pass
##############################################################################
#This will be a screen for all of the fitness functions
class getFitScreen(Screen):
top_layout = ObjectProperty(None)
dd_btn = ObjectProperty(None)
top_layout2 = ObjectProperty(None)
dd_btn2 = ObjectProperty(None)
def __init__(self,*args,**kwargs):
super(getFitScreen, self).__init__(*args, **kwargs)
#everything undere this is new code from stackover flow and it works for one. Stops working at GYM
self.drop_down = CustomDropDownTime()
dropdown = DropDown()
#time availability dropdown
time = ['15-30mins', '30-60mins', '60-90mins','90-120mins']
for times in time:
btn = Button(text='%r' %times, size_hint_y=None, height=30)
btn.bind(on_release=lambda btn: dropdown.select(btn.text))
dropdown.add_widget(btn)
mainbutton = Button(text='Time Available', size_hint=(1, 1))
mainbutton.bind(on_release=dropdown.open)
dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
###############################################################################
#This will be a screen for the charts
class graphScreen(Screen):
def __init__(self,**kwargs):
super(graphScreen, self).__init__(**kwargs)
################################################################################
class KivyTutorApp(App):
def __init__(self, **kwargs):
super(KivyTutorApp, self).__init__(**kwargs)
Window.bind(on_keyboard=self.onBackBtn)
def onBackBtn(self, window,key,*args):
#if user presses back button
#27 is the numerical code for back button
if key == 27:
return self.root.onBackBtn()
def build(self):
return KivyTutorRoot()
# this next part is so that we can 'get text' from this .py file when running from our .kv file
def getText(self):
# you need markup: True to use references like these
return ("Hey there! \nThis App was built using "
"[b][ref=kivy]kivy[/ref][/b]\n"
"Feel free to look at the source code "
"[b][ref=sour"
"ce]here[/ref][/b].\n"
"This app is under the [b][ref=mit]MIT License[/ref][/b]\n"
"Me: [b][ref=website]#kevin_adrian95[/ref][/b]")
# this next part is going to make the actual references
def on_ref_press(self, instance, ref):
dict = {
"source": "https://github.com/gopar/Kivy-Tutor",
# youre going to want to change this to your own github when you finish.
"website": "https://www.instagram.com/kevin_adrian95/",
"kivy": "https://kivy.org/#home",
"mit": "https://github.com/gopar/kivy-Tutor/blob/master/LICENSE"
}
webbrowser.open(dict[ref])
KivyTutorApp().run()
.kv file:
<WrappedLabel#Label>:
size_hint_y: None
height: self.texture_size[1]+(self.texture_size[1]/2)
markup: True
<CustomDropDownTime>:
Button:
text: '15-30 mins'
size_hint_y: None
height: 44
on_release: root.select('15-30mins')
Button:
text: '30-60 mins'
size_hint_y: None
height: 44
on_release: root.select('30-60min')
Button:
text: '60-90 mins'
size_hint_y: None
height: 44
on_release: root.select('60-90mins')
Button:
text: '90-120 mins'
size_hint_y: None
height: 44
on_release: root.select('90-120mins')
<CustomDropDownGym>:
Button:
text: 'Yes'
size_hint_y: None
height: 44
on_release: root.select('Yes')
Button:
text: 'No'
size_hint_y: None
height: 44
on_release: root.select('No')
< KivyTutorRoot >:
orientation: "vertical"
ActionBar:
ActionView:
ActionPrevious:
title: 'Kevin Adrian'
with_previous: False
ActionOverflow:
ActionButton:
text: "Settings"
on_press: app.open_settings()
ScreenManager:
id: kivy_screen_manager
StartScreen:
name: "start_screen"
AboutScreen:
id: about_screen
name: "about_screen"
getFitScreen:
id: getFitScreen
name: "getFitScreen"
<StartScreen#Screen>:
BoxLayout:
#settings
orientation: "vertical"
padding: root.width * .2, root.height*.1
spacing: min(root.width, root.height)*.1
WrappedLabel:
text: "[b] Kevin Adrian [/b]"
font_size: min(root.height, root.width) /10
Button:
text: "Get Fit"
font_size: 35
on_release: app.root.changeScreen(self.text.lower())
Button:
text: "Create User"
font_size: 20
Button:
text: "About this app"
on_release: app.root.changeScreen(self.text.lower())
<AboutScreen#Screen>:
BoxLayout:
padding: root.width * .02, root.height*.02
Label:
text: app.getText()
halign: "center"
markup: True
font_size: root.height / 20
text_size: self.width, None
center_y: .5
on_ref_press: app.on_ref_press(*args)
<getFitScreen>:
id: getFitScreen
top_layout: topLayoutID
dd_btn: btn_ddID
BoxLayout:
id: topLayoutID
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .95}
Button:
id: btn_ddID
text: 'Time Availablity'
on_release: root.drop_down.open(self)
Button:
id: btn_ddID2
text: 'Gym Access'
on_release: root.drop_down.open(self)
Button:
text: 'Training Level'
on_release: root.drop_down.open(self)
class getFitScreen(Screen):
def __init__(self, *args, **kwargs):
super(getFitScreen, self).__init__(*args, **kwargs)
self.dropdown = DropDown()
self.dropdown1 = DropDown()
self.dropdown2= DropDown()
time = ['15-30mins', '30-60mins', '60-90mins', '90-120mins']
level = [1, 2, 3]
access = [True, False]
for times in time:
btn = Button(text='%r' % times, size_hint_y=None, height=30)
btn.bind(on_release=lambda btn: self.dropdown.select(btn.text))
self.dropdown.add_widget(btn)
mainbutton = Button(text='Time Available', size_hint=(1, 1))
mainbutton.bind(on_release=self.dropdown.open)
self.dropdown.bind(on_select=lambda instance, x: setattr(mainbutton, 'text', x))
for levels in level:
btn1 = Button(text='%r' % levels, size_hint_y=None, height=30)
btn1.bind(on_release=lambda btn1: self.dropdown1.select(btn1.text))
self.dropdown1.add_widget(btn1)
mainbutton1 = Button(text='Training Level', size_hint=(1, 1))
mainbutton1.bind(on_release=self.dropdown1.open)
self.dropdown1.bind(on_select=lambda instance, x: setattr(mainbutton1, 'text', x))
for bool in access:
btn2 = Button(text = '%r' % bool, size_hint_y=None,height=30)
btn2.bind(on_release = lambda btn2 : self.dropdown2.select(btn2.text))
self.dropdown2.add_widget(btn2)
mainbutton2 = Button(text = 'Gym Access',size_hint=(1,1))
mainbutton2.bind(on_release=self.dropdown2.open)
self.dropdown2.bind(on_select=lambda instance, x: setattr(mainbutton2,'text', x))
So this is the change to the .py file above. I left out the rest of the code, just the code for the screen that I am trying to put the dropdown menus into.
<getFitScreen>:
id: getFitScreen
top_layout: topLayoutID
dd_btn: btn_ddID
BoxLayout:
id: topLayoutID
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .95}
Button:
id: btn_ddID
text: 'Time Availablity'
on_release: root.dropdown.open(self)
Button:
id: btn2_ddID2
text: 'Training Level'
on_release: root.dropdown1.open(self)
Button:
id: btn3_ddID3
text: 'Gym Access'
on_release: root.dropdown2.open(self)
Then the only change I made to the .kv file was at the very end, corresponding to the screen I wanted to make adjustments to. Basically I just had to change a couple variable names.
So I've been having a little trouble with ListView. What I have been trying to do is make an API call to ebay. Then I setup a ListView with the results. However, here's the issue. The first time I do a search and it brings me to the results page, it only shows one result. If I scroll way down I will see another result or two pop up, but they disappear right away. Then I will go back to the main page and do the exact same search, however, this time when it goes to the results page, it lists a lot more content. If I scroll down everything seems fine at first, but then the spacing will mess up, there will be a huge gap of nothingness, then the results will be closer together. Here are a few pictures to better explain the issue (sorry I threw the code together real quick to test it.. been trying forever to fix this issue though)
kv file
#: import ListItemLabel kivy.uix.listview.ListItemLabel
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import CompositeListItem kivy.uix.listview.CompositeListItem
#: import AsyncImage kivy.uix.image.AsyncImage
#: import SelectableView kivy.uix.listview.SelectableView
#: import BoxLayout kivy.uix.boxlayout.BoxLayout
#: import main main
ScreenManagement:
<ScreenManagement>:
SearchForm
ResultPage
<SearchForm>:
name: "search_screen"
search_box:search_box
search_button:search_button
BoxLayout:
orientation: 'vertical'
Label:
text: "FLEA MARKET FLIP"
font_size: 45
pos_hint: {"center_x":0.5, "center_y":1}
BoxLayout:
orientation: 'vertical'
size_hint_y: None
height: '100dp'
TextInput:
id: search_box
text: ""
size_hint_x: 0.5
pos_hint: {"center_x":0.5, "center_y":0.5}
Button:
text: "Price It!"
id:search_button
on_release:
root.manager.get_screen("result_page").results_list.adapter.data.clear()
root.manager.get_screen("result_page").results_list.adapter.data.extend([s for s in app.root.ebay(search_box.text)])
root.manager.get_screen("result_page").do_layout()
root.manager.get_screen("result_page").results_list._trigger_reset_populate()
app.root.current = "result_page"
<ResultPage>:
name: "result_page"
orientation: 'vertical'
results_list:results_list
ListView:
padding:(100,110)
id: results_list
adapter: ListAdapter(data=[], cls=main.Ebay_Item, args_converter=root.args_converter, allow_empty_selection=False)
FloatLayout:
Button:
size_hint: [0.2,0.1]
spacing: 50
pos_hint: {"x":0, "y":0}
text: "Back"
on_release:
app.root.current = "search_screen"
<Ebay_Item>:
orientation: "vertical"
cols: 3
#spacing: 60
row_force_default: True
row_default_height: 40
Label:
text: root.results[0]
size: self.texture_size
text_size: self.width, self.height
Label:
text: root.results[1]
size: self.texture_size
AsyncImage:
allow_stretch: True
size_hint_y: None
size_hint_x: None
source: root.results[2]
size: self.texture_size
main.py
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ListProperty
from scripts.ebay import ebay_search
from kivy.uix.listview import SelectableView
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.app import App
class PriceItApp(App):
pass
class ScreenManagement(ScreenManager):
def ebay(self, data):
return ebay_search(data)
class SearchForm(Screen):
pass
class ResultPage(Screen):
def args_converter(self, index, data_item):
title, price, picture = data_item
return {"results": [title, price, picture]}
class Ebay_Item(SelectableView, GridLayout):
results = ListProperty()
if __name__ == "__main__":
PriceItApp().run()
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.