In summary, the program takes a bunch of list data and populates a row to describe a task with some actions attached to it.
After much work I got scrollview working with Floatlayout somewhat. The issue is that the list of rows of labels are stuck at the bottom part of the screen as in the screenshot. When you run the code, put your curser towards the very bottom of the screen and scrolldown. The widgets will appear and it scrolls (yay). You will then have experienced my two problems
- Why do the widgets start outside of view
- Why is the layout stuck at the bottom.
I am looking at how to make that floatlayout be 12/15 from the top of the screen, and for the widgets to be in frame from start.
import kivy
kivy.require('2.1.0') # replace with your current kivy version !
from kivy.config import Config
Config.set('kivy', 'keyboard_mode', 'systemanddock')
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown
from kivy.uix.textinput import TextInput
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ListProperty, ObjectProperty
from kivy.factory import Factory
from kivy.uix.scrollview import ScrollView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from os import path, getcwd
from datetime import date, timedelta, datetime
from calendar import monthrange
from dateutil.parser import parse
from dateutil.relativedelta import relativedelta
class MainScreen(Screen):
headings = ['Bucket', 'Description', 'Due Date', 'Priority', 'Repeat', 'Person', 'Complete']
lst = [['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None']]
#all
lst_widgets = []
filtercomplete = 0
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
def on_enter(self):
self.InitialiseWidgets()
# Draw all widgets on screen
def InitialiseWidgets(self):
self.clear_widgets()
self.WidgetScroller()
self.WidgetTable()
def WidgetScroller(self):
scrlv = ScrollView(size_hint=(1,None), do_scroll_x = False)
self.layout = FloatLayout(size_hint=(1, None), size=(Window.width, Window.height))
scrlv.add_widget(self.layout)
self.add_widget(scrlv)
def WidgetTable(self):
num_headings = len(self.headings)
row_data = []
self.tabledata = []
y_pos = 12/15
if len(self.lst) != 0: # if list is not empty
for x in range(len(self.lst)): # for each task in list
x_pos = 0/10
for y in range(num_headings): # for each property of task
if y == self.headings.index('Complete'):
if self.filtercomplete == 1: # if filtering by completed,
row_data.append(Label(text = " ", size_hint = (1/20, 1/15), pos_hint = {'x':8/10, 'top':y_pos})) # do not add finish button
else:
row_data.append(Button(text = "FINISH", size_hint = (1/20, 1/15), pos_hint = {'x':8/10, 'top':y_pos})) # else add finish button
else:
print(self.lst[x][y])
row_data.append(Label(text = self.lst[x][y], size_hint = (1/10, 1/15), pos_hint = {'x':x_pos, 'top':y_pos})) # create label for task properties
if y == 1:
x_pos = x_pos + 3/10
else:
x_pos = x_pos + 1/10
row_data.append(Button(text = "PUSH", size_hint = (1/20, 1/15), pos_hint = {'x':8.5/10, 'top':y_pos})) # Action buttons
row_data.append(Button(text = "EDIT", size_hint = (1/20, 1/15), pos_hint = {'x':9/10, 'top':y_pos}))
row_data.append(Button(text = "DEL", size_hint = (1/20, 1/15), pos_hint = {'x':9.5/10, 'top':y_pos}))
y_pos = y_pos - 1/15
self.tabledata.append(row_data.copy())
row_data *= 0
# Draw widgets
for widget_row in self.tabledata:
for widget in widget_row:
self.layout.add_widget(widget)
class MyApp(App):
def build(self):
# Create the screen manager
sm = ScreenManager()
sm.add_widget(MainScreen(name='main'))
return sm
if __name__ == '__main__':
MyApp().run()
Advertisement
Answer
Your questions:
- The widgets are outside the initial view because you are positioning the widgets using
pos_hint
of{'y': 12/15}
to start. That sets the largesty
pos_hint
at0.8
, and you decrease it from there. So the layout above0.8
is empty. Also, yourScrollView
hassize_hint
set toNone
fory
. That causes the default value of100
to be used as itsheight
, so only the top100
pixels of the layout can fit. - The default
pos
for wigets is[0,0]
. Since you don’t set the position of theScrollView
, it will get the defaultpos
.
I suggest using a Layout
that will do a lot of the work for you. Perhaps a GridLayout
. And you can define the look of the MainScreen
using kv
. Here is a kv
that does that:
<MainScreen>:
ScrollView:
do_scroll_x: False
pos_hint: {'top': 0.75}
size_hint: 1, 0.5
GridLayout:
id: grid
cols: 10
size_hint_y: None # required to allow automatice size adjustment
row_default_height: 50 # each row in the GridLayout will be given at least 50 pixels
height: self.minimum_height # the GridLayout will adjust its size as needed
If you load the above kv
before creating the MainScreen()
, then you can simplify your py
code for the MainScreen
class:
class MainScreen(Screen):
headings = ['Bucket', 'Description', 'Due Date', 'Priority', 'Repeat', 'Person', 'Complete']
lst = [['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None'],
['None','','29-May-2022','Low','None','None']]
#all
lst_widgets = []
filtercomplete = 0
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.widgets_drawn = False
def on_enter(self):
if not self.widgets_drawn:
self.InitialiseWidgets()
self.widgets_drawn = True
# Draw all widgets on screen
def InitialiseWidgets(self):
# self.clear_widgets()
# self.WidgetScroller()
self.WidgetTable()
# def WidgetScroller(self):
#
# self.scrlv = ScrollView(size_hint=(1,None), do_scroll_x = False, pos_hint={'top': 0.75})
# self.layout = FloatLayout(size_hint=(1, None), size=(Window.width, Window.height))
# self.scrlv.add_widget(self.layout)
# self.add_widget(self.scrlv)
def WidgetTable(self):
self.layout = self.ids.grid # this is the GridLayout
num_headings = len(self.headings)
row_data = []
self.tabledata = []
if len(self.lst) != 0: # if list is not empty
for x in range(len(self.lst)): # for each task in list
for y in range(num_headings): # for each property of task
if y == self.headings.index('Complete'):
if self.filtercomplete == 1: # if filtering by completed,
row_data.append(Label(text = " ", size_hint = (1/20, None))) # do not add finish button
else:
row_data.append(Button(text = "FINISH", size_hint = (1/20, None))) # else add finish button
else:
print(self.lst[x][y])
row_data.append(Label(text = self.lst[x][y], size_hint = (1/10, None))) # create label for task properties
row_data.append(Button(text = "PUSH", size_hint = (1/20, None))) # Action buttons
row_data.append(Button(text = "EDIT", size_hint = (1/20, None)))
row_data.append(Button(text = "DEL", size_hint = (1/20, None)))
self.tabledata.append(row_data.copy())
row_data *= 0
# Draw widgets
for widget_row in self.tabledata:
for widget in widget_row:
self.layout.add_widget(widget)
Note that all the position information is removed from the created Buttons
and Labels
, since the GridLayout
handles that.
Another thing to note is that you are using on_enter()
to trigger the widget drawing. That will get triggered every time you enter
the MainScreen
, so I have added a widgets_drawn
attribute to avoid adding the same widgets every time the MainScreen
is entered.