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.