首页 > 解决方案 > 如何在 Kivy 的 .kv 文件中使用 for 循环创建标签?

问题描述

我正在 Kivy 中开发 PDF 解析应用程序。我在 .py 文件中使用屏幕管理器。在其中一个屏幕中,我选择 pdf 文件并将其添加到列表中,更新另一个屏幕(“文件”)中的功能,然后切换到该屏幕。我的 .py 文件如下:-

import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
kivy.require("1.11.1")

from kivy.uix.floatlayout import FloatLayout
from kivy.config import Config


from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
Config.set('graphics', 'resizable', True)

from PyPDF2 import PdfFileReader, PdfFileWriter

FILE_LIST = []
PAGE_LIST = []
OUTPUT_LIST = []


def add_output_list(page_name):
    for item in PAGE_LIST:
        if item[0] == page_name:
            if item[-1] not in OUTPUT_LIST:
                OUTPUT_LIST.append(item[-1])
    print(OUTPUT_LIST)


class FinalPage(GridLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.cols = 1


class ParsingPage(GridLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.cols = 4

    def update_pages(self):
        for item in PAGE_LIST:
            self.inside = GridLayout()
            self.inside.cols = 3

            self.inside.select = Button(text="sel")
            self.inside.add_widget(self.inside.select)
            self.inside.select.bind(on_press=lambda x: self.select_button(item[-1]))

            self.inside.clock = Button(text="c")
            self.inside.add_widget(self.inside.clock)

            self.inside.anticlock = Button(text="ac")
            self.inside.add_widget(self.inside.anticlock)

            self.add_widget(self.inside)
            self.add_widget(Label(text=f'{item[0]}'))
        self.inside_2 = GridLayout()
        self.inside_2.cols = 2
        self.inside_2.done = Button(text="Done")
        self.inside_2.add_widget(self.inside_2.done)
        self.inside_2.done.bind(on_press=self.done_button)

        self.inside_2.cancel = Button(text="Cancel")
        self.inside_2.add_widget(self.inside_2.cancel)
        self.inside_2.cancel.bind(on_press=self.cancel_button)

    def select_button(self, page_name):
        add_output_list(page_name)

    def done_button(self):
        pass

    def cancel_button(self):
        pass


class SelectionPage(Widget):

    def select(self, *args):
        try:
            if args[1][0].split('.')[-1] != 'pdf':
                self.label.text = 'You can only select a PDF File.'
            else:
                self.label.text = args[1][0]
        except:
            pass

    def add_button(self):
        FILE_LIST.append(self.label.text)
        pdf_app.files_page.update_files(self.label.text)
        pdf_app.screen_manager.current = 'Files'

    def next_button(self):
        pdf_app.screen_manager.current = 'Files'


class FilesPage(Widget):
    def update_files(self):
        return FILE_LIST


class BrowsePage(Widget):

    def browse_button(self):
        pdf_app.screen_manager.current = 'Selection'


class PdfParserApp(App):
    FILE_LIST = []

    def build(self):

        self.screen_manager = ScreenManager()
        self.browse_page = BrowsePage()
        screen = Screen(name='Browse')
        screen.add_widget(self.browse_page)
        self.screen_manager.add_widget(screen)

        # Info page
        self.selection_page = SelectionPage()
        screen = Screen(name='Selection')
        screen.add_widget(self.selection_page)
        self.screen_manager.add_widget(screen)

        self.files_page = FilesPage()
        screen = Screen(name='Files')
        screen.add_widget(self.files_page)
        self.screen_manager.add_widget(screen)

        self.parsing_page = ParsingPage()
        screen = Screen(name='Parsing')
        screen.add_widget(self.parsing_page)
        self.screen_manager.add_widget(screen)

        return self.screen_manager


if __name__ == '__main__':
    pdf_app = PdfParserApp()
    pdf_app.run()

FILE_LIST 是所有类之外的变量(因此它可以在类之间使用。

现在,我想遍历这个 FILE_LIST 并在下一个屏幕上创建标签。但我想使用 .kv 文件来实现,这样我就可以在整个应用程序中保持设计的一致性。我的 .kv 文件是:-

#, kv file implementation
#:import Label kivy.uix.label.Label

<BrowsePage>:
    GridLayout:
        size : root.width-200, root.height-200
        pos : 100, 100
        cols : 1
        Label :
            text: "Welcome to PDF Parser"
            color: [ 66/255, 103/255, 178/255, 1]
            font_size: 38
            size_hint : (0.2, 0.5)
        Label :
            text : "Select the file(s)."
            color: [ 66/255, 103/255, 178/255, 1]
            font_size: 20
            size_hint : (0.2, 0.5)
        AnchorLayout:
            anchor_x : "center"
            Button:
                text : "Browse"
                size_hint : (.15, .15)

                on_press : root.browse_button()

<SelectionPage>:

    label: label


    GridLayout:
        size : root.width, root.height
        cols :1

        FileChooserIconView:

            pos_hint: {"x":0, "top" : 1}
            on_selection: root.select(*args)

        Label:

            id: label
            size_hint : (.1, .1)

        GridLayout:
            cols : 3
            size_hint : (1, .15)

            AnchorLayout:
                anchor_x : "center"
                Button:
                    text : "Cancel"
                    size_hint : (.15, .15)

            AnchorLayout:
                anchor_x : "center"
                Button:
                    text : "Add"
                    size_hint : (.15, .15)
                    on_press: root.add_button()
            AnchorLayout:
                anchor_x : "center"
                Button:
                    text : "Next"
                    size_hint : (.15, .15)
                    on_press: root.next_button()



<FilesPage>
    GridLayout:
        size : root.width, root.height

        cols: 1
        on_parent:
            for i in root.update_files(): txt = "Label {0}".format(i); self.add_widget(Label(text = txt, text_size=(cm(2), cm(2)), pos=self.pos,
            id=txt, color=(1,1,1,1)))


我假设当我切换屏幕时,它会以某种方式创建一个新的空列表。循环代码没有任何错误,就好像我在 FilePage 中硬编码一个列表,然后出现标签。

任何帮助将不胜感激。

标签: pythonkivykivy-language

解决方案


此响应基于问题标题。我也同意@Nykakin,这是将 UI 逻辑与 UI 设计分开的好习惯。

我个人对此采取了不同的方法。

add_label.kv

<MainView>:
    orientation: 'vertical'


<MyLabel>:
    text: root.label_text
    color: root.label_color

然后在python中:

添加标签.py

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.properties import StringProperty, ListProperty

MY_FILE_LIST = [
    {
        'label_text': 'foo label_0',
        'label_color': [1, .5, .4, 1]
    },
    {
        'label_text': 'bar label_1',
        'label_color': [.2, .4, .3, 1]
    },
    {
        'label_text': 'foobar label_2',
        'label_color': [.2, .9, .1, 1]
    },
]

Builder.load_file('add_label.kv')


class MainView (BoxLayout):

    def __init__(self, **kwargs):
        super(MainView, self).__init__(**kwargs)
        for file in MY_FILE_LIST:
            lbl = MyLabel()
            lbl.label_text = file['label_text']
            lbl.label_color = file['label_color']
            self.add_widget(lbl)


class MyLabel(Label):
    label_text = StringProperty()
    label_color = ListProperty()


class LabelApp (App):

    def build(self):
        return MainView()


if __name__ == '__main__':
    LabelApp().run()

如果您一次在视图上添加少量小部件(我不超过大约 10 ~ 30 个),则此方法将起作用。如果您的数据集大于 30 点,那么您的应用程序可能会挂起,直到在视图上创建和添加所有小部件实例。此外,这种方法更难更新MY_FILE_LIST,并且更新会反映在 UI 上。并非不可能,但要做到这一点有点棘手。

RecycledView是更适合大型数据集的解决方案。或者,如果您希望更新MY_FILE_LIST会反映在 UI 上。


推荐阅读