Как создать метки, используя цикл for в файле Kivy's .kv? - PullRequest
0 голосов
/ 10 октября 2019

Я работаю над приложением для разбора 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)))


IПредположим, что когда я переключаю экран, он каким-то образом создает новый пустой список. Код зацикливания не содержит ошибок, как будто я жестко кодирую список в моей FilePage, тогда появляются метки.

Любая помощьбудет оценено.

Ответы [ 2 ]

0 голосов
/ 14 октября 2019

Этот ответ основан на названии вопроса. Также я согласен с @Nykakin, который является хорошей практикой для отделения вашей логики пользовательского интерфейса от дизайна пользовательского интерфейса.

Лично я использую другой подход к этому.

add_label.kv

<MainView>:
    orientation: 'vertical'


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

затем в python:

add_label.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, и это обновление будет отражено в пользовательском интерфейсе. Это не невозможно, но сделать это немного сложнее.

RecycledView , более подходящее решение для больших наборов данных. Или, если вы ожидаете, что обновления в MY_FILE_LIST будут отражены в пользовательском интерфейсе.

0 голосов
/ 11 октября 2019

Я лично считаю, что это признак плохого дизайна, когда слишком много кода помещается в файлы KV. Должно быть четкое разделение между структурой виджетов (как описано в файле KV) и реальной логикой, которая должна быть реализована в Python. В этом случае мы могли бы использовать событие on_enter для создания меток при отображении экрана FilesPage. Это выглядело бы следующим образом

class PdfParserApp(App):    
    def build(self):
        # ...

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

        # ...

    def enter_files_page(self, files_page, *args):
        for f in FILE_LIST:
            files_page.add_widget(Label(text=f))
...