带有 FloatLayout 的 Kivy Scrollview 将自身定位在屏幕底部

Kivy Scrollview with FloatLayout Is Positioning itself at the bottom of the screen

总而言之,该程序获取了一堆列表数据并填充了一行来描述一个带有一些附加操作的任务。

经过大量工作后,我让 scrollview 在某种程度上使用了 Floatlayout。问题是标签行列表卡在屏幕底部,如屏幕截图所示。当您 运行 代码时,将光标放在屏幕的最底部并向下滚动。小部件将出现并滚动(耶)。然后你就会遇到我的两个问题

  1. 为什么小部件在视图之外启动
  2. 为什么布局卡在底部。

我正在研究如何使浮动布局距屏幕顶部 12/15,并使小部件从一开始就在框架中。

enter image description here

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()

您的问题:

  1. 小部件位于初始视图之外,因为您开始使用 pos_hint{'y': 12/15} 定位小部件。这将最大的 y pos_hint 设置为 0.8,然后从那里减少它。所以 0.8 上面的布局是空的。此外,您的 ScrollView 已针对 ysize_hint 设置为 None。这导致 100 的默认值用作其 height,因此只有布局的顶部 100 像素可以适合。
  2. wigets 的默认 pos[0,0]。由于您没有设置 ScrollView 的位置,它将获得默认的 pos.

我建议使用 Layout,它可以为您完成很多工作。也许 GridLayout。您可以使用 kv 定义 MainScreen 的外观。这里有一个 kv 可以做到这一点:

<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

如果您在创建 MainScreen() 之前加载上面的 kv,那么您可以简化 MainScreen class 的 py 代码:

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)

请注意,所有位置信息都已从创建的 ButtonsLabels 中删除,因为 GridLayout 会处理这些信息。

另一件需要注意的事情是,您正在使用 on_enter() 来触发小部件绘制。每次你 enter MainScreen 时都会触发,所以我添加了一个 widgets_drawn 属性以避免每次输入 MainScreen 时添加相同的小部件。