|
@@ -0,0 +1,516 @@
|
|
|
|
+**Создание оконного приложения на Python с использовением Kivy/KivyMD**
|
|
|
|
+***
|
|
|
|
+**Установка Kivy/KivyMD на Windows**
|
|
|
|
+***
|
|
|
|
+Для установки нам потребуется всего две `команды`:
|
|
|
|
+```
|
|
|
|
+pip install kivy
|
|
|
|
+```
|
|
|
|
+```
|
|
|
|
+pip install kivymd
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Установка Kivy/KivyMD на MacOS**
|
|
|
|
+***
|
|
|
|
+Для установки `Kivy` на `Mac`, нам нужно установить версию языка `Python` от `3.6` до `3.9`, так как начиная с версии `3.10`, Kivy перестает работать на `MacOS`. Эта проблема затрагивает только пользователей `MacOS`, с `Windows` же таких проблем нет.
|
|
|
|
+***
|
|
|
|
+```
|
|
|
|
+pip install kivy
|
|
|
|
+```
|
|
|
|
+```
|
|
|
|
+pip install kivymd
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Скачать** Python любой версии можно с [официального сайта](https://www.python.org/downloads/release/python-390/).
|
|
|
|
+***
|
|
|
|
+**В чем же отличие Kivy от KivyMD?**
|
|
|
|
+***
|
|
|
|
+`Kivy:`
|
|
|
|
+
|
|
|
|
+* **Базовый фреймворк**: Kivy - основа, которая предоставляет все основные инструменты для создания UI, обработки событий, работы с графикой, анимацией и т.д.
|
|
|
|
+* **Собственный стиль**: Kivy имеет свой собственный, довольно минималистичный стиль виджетов.
|
|
|
|
+* **Гибкость**: Kivy предоставляет большую гибкость в настройке внешнего вида приложения. Вы можете создавать свои собственные виджеты и стилизовать их как угодно.
|
|
|
|
+***
|
|
|
|
+
|
|
|
|
+`KivyMD:`
|
|
|
|
+
|
|
|
|
+* **Надстройка над Kivy**: KivyMD - это коллекция виджетов, созданная поверх Kivy, которая реализует [Material Design](https://m3.material.io/) от Google.
|
|
|
|
+* **Современный внешний вид**: Виджеты KivyMD соответствуют гайдлайнам Material Design, что обеспечивает современный и привлекательный внешний вид приложения.
|
|
|
|
+* **Готовые компоненты**: KivyMD предоставляет широкий набор готовых к использованию компонентов Material Design, что упрощает разработку.
|
|
|
|
+* **Меньше гибкости**: KivyMD предлагает меньше возможностей для кастомизации, чем Kivy, так как он следует определенным правилам Material Design.
|
|
|
|
+***
|
|
|
|
+`Вывод:`
|
|
|
|
+
|
|
|
|
+* Если вам нужна максимальная гибкость в настройке дизайна, и вы готовы создавать свой собственный стиль, выбирайте `Kivy`.
|
|
|
|
+* Если вам нужен современный дизайн в стиле Material Design, и вы хотите использовать готовые компоненты для ускорения разработки, выбирайте `KivyMD`.
|
|
|
|
+***
|
|
|
|
+**Перейдем к созданию приложеяния нa KivyMD**
|
|
|
|
+***
|
|
|
|
+**Напишем обычное окно с тектом.**
|
|
|
|
+
|
|
|
|
+Для начала нам нужно импортировать все необходимые для нас ресурсы, для создания приложения.
|
|
|
|
+***
|
|
|
|
+* импортируем класс `MDApp`, который является основой для приложений KivyMD.
|
|
|
|
+```
|
|
|
|
+from kivymd.app import MDApp
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+* импортируем класс `MDLabel` для создания текстовой метки в стиле Material Design.
|
|
|
|
+```
|
|
|
|
+from kivymd.uix.label import MDLabel
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Создание класса приложения**
|
|
|
|
+
|
|
|
|
+* создаем класс `MyMDApp`, наследуемый от `MDApp`.
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+class MyMDApp(MDApp):
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Метод** `build()`
|
|
|
|
+
|
|
|
|
+* определяет содержимое окна приложения
|
|
|
|
+```
|
|
|
|
+def build(self):
|
|
|
|
+```
|
|
|
|
+* создаем объект `MDLabel` с текстом, выровненным по центру, и возвращаем его.
|
|
|
|
+```
|
|
|
|
+return MDLabel(text="Любой другой текст", halign="center")
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+
|
|
|
|
+**Запуск приложения**
|
|
|
|
+* стандартная проверка для запуска скрипта.
|
|
|
|
+```
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
+```
|
|
|
|
+* создаем экземпляр класса `MyMDApp` и запускаем приложение.
|
|
|
|
+```
|
|
|
|
+MyMDApp().run()
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Полный код выглядит таким образом:**
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+from kivymd.app import MDApp
|
|
|
|
+from kivymd.uix.label import MDLabel
|
|
|
|
+
|
|
|
|
+class MyMDApp(MDApp):
|
|
|
|
+ def build(self):
|
|
|
|
+ return MDLabel(text="Любой другой текст", halign="center")
|
|
|
|
+
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
+ MyMDApp().run()
|
|
|
|
+```
|
|
|
|
+![](./img/1.png)
|
|
|
|
+
|
|
|
|
+***
|
|
|
|
+Совсем забыл сказать, что в Kivy мы используем ООП (Объектно Ориентированное Программирование). Если вы с ним не знакомы, советую прочитать познавательную [статью](https://proglib.io/p/python-oop) на эту тему.
|
|
|
|
+***
|
|
|
|
+
|
|
|
|
+**Думаю суть уловили, буду приступать к своей работе.**
|
|
|
|
+
|
|
|
|
+Импорт главного окна приложения
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+from kivymd.app import MDApp
|
|
|
|
+```
|
|
|
|
+Импорт бокслояута для добавления в него виджетов
|
|
|
|
+```
|
|
|
|
+from kivy.uix.boxlayout import BoxLayout
|
|
|
|
+```
|
|
|
|
+Импорт для изображения
|
|
|
|
+```
|
|
|
|
+from kivy.uix.image import Image
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+* главный класс
|
|
|
|
+```
|
|
|
|
+class SearchBoxLayout(BoxLayout):
|
|
|
|
+ def __init__(self, **kwargs):
|
|
|
|
+ super().__init__(**kwargs)
|
|
|
|
+ self.orientation = 'vertical'
|
|
|
|
+ self.padding = [50, 0, 50, 50]
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+`def __init__(self, **kwargs):` # Инициализация класса
|
|
|
|
+
|
|
|
|
+`super().__init__(**kwargs)` # Вызов конструктора родительского класса BoxLayout
|
|
|
|
+
|
|
|
|
+`self.orientation = 'vertical'` # Установка ориентации элементов на вертикальную
|
|
|
|
+
|
|
|
|
+`self.padding = [50, 0, 50, 50]` # Отступы вокруг элементов
|
|
|
|
+
|
|
|
|
+***
|
|
|
|
+* добавление изображения
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+self.image = Image(
|
|
|
|
+ source='net.jpg',
|
|
|
|
+ size_hint_y=0.3
|
|
|
|
+ )
|
|
|
|
+ self.add_widget(self.image)
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+`self.image = Image()` # Создание объекта Image
|
|
|
|
+
|
|
|
|
+`source='net.jpg',` # Путь к изображению
|
|
|
|
+
|
|
|
|
+`size_hint_y=0.3` # Относительный размер изображения по вертикали
|
|
|
|
+
|
|
|
|
+`self.add_widget(self.image)` # Добавление изображения в бокслояут
|
|
|
|
+
|
|
|
|
+**Основной код выглядит так:**
|
|
|
|
+```
|
|
|
|
+from kivymd.app import MDApp
|
|
|
|
+from kivy.uix.boxlayout import BoxLayout
|
|
|
|
+from kivy.uix.image import Image
|
|
|
|
+
|
|
|
|
+class SearchBoxLayout(BoxLayout):
|
|
|
|
+ def __init__(self, **kwargs):
|
|
|
|
+ super().__init__(**kwargs)
|
|
|
|
+ self.orientation = 'vertical'
|
|
|
|
+ self.padding = [50, 0, 50, 50]
|
|
|
|
+
|
|
|
|
+ # Импорт фотографии
|
|
|
|
+ self.image = Image(
|
|
|
|
+ source='net.jpg',
|
|
|
|
+ size_hint_y=0.3
|
|
|
|
+ )
|
|
|
|
+ self.add_widget(self.image)
|
|
|
|
+
|
|
|
|
+class MyApp(MDApp):
|
|
|
|
+ def build(self):
|
|
|
|
+ return SearchBoxLayout()
|
|
|
|
+
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
+ MyApp().run()
|
|
|
|
+```
|
|
|
|
+![](./img/2.png)
|
|
|
|
+***
|
|
|
|
+* создание таблицы и импорт данных из `json`
|
|
|
|
+
|
|
|
|
+это создание обычной таблицы
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+self.data_table = MDDataTable(
|
|
|
|
+ size_hint=(0.9, 0.9),
|
|
|
|
+ rows_num=10,
|
|
|
|
+ column_data=[
|
|
|
|
+ ("Название", dp(30)),
|
|
|
|
+ ("Год", dp(20)),
|
|
|
|
+ ("Цена", dp(20)),
|
|
|
|
+ ("Цвет", dp(20)),
|
|
|
|
+ ("Повреждения", dp(30)),
|
|
|
|
+ ("Дата СТО", dp(30))
|
|
|
|
+ ],
|
|
|
|
+ row_data=[
|
|
|
|
+ ("Toyota Trueno AE86", "1995", "15000", "white", "True", "19/04/2024"),
|
|
|
|
+ ("Toyota Supra A80", "1996", "30000", "black", "False", "09/02/2024"),
|
|
|
|
+ ("Nissal Skyline R34", "1996", "25000", "orange", "False", "10/07/2024"),
|
|
|
|
+ ("Nissan Silvia S15", "1999", "20000", "gray", "False", "20/03/2024"),
|
|
|
|
+ ("Toyota Camry 3.5", "2020", "20000", "blue", "False", "19/04/2024"),
|
|
|
|
+ ("Audi RS 6", "2016", "30000", "lightblue", "True", "19/04/2024"),
|
|
|
|
+ ("Трактор LOVOL TE354 HT", "2024", "66666", "brown", "False", "19/04/2024"),
|
|
|
|
+ ("BMW M5 F90", "2019", "30000", "black", "True", "19/04/2024"),
|
|
|
|
+ ("BMW E36", "2006", "35000", "red", "False", "19/04/2024"),
|
|
|
|
+ ("Daewoo Matiz", "2010", "20000", "pink", "True", "19/04/2024"),
|
|
|
|
+ ]
|
|
|
|
+ )
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+Это создание таблицы с `json`
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+ # Прокрутка для таблицы
|
|
|
|
+ self.scrollview = ScrollView(size_hint_y=0.7)# Создание прокручиваемого контейнера для таблицы
|
|
|
|
+ self.add_widget(self.scrollview)# Добавление прокрутки в основной бокслояут
|
|
|
|
+
|
|
|
|
+ # Создание таблицы
|
|
|
|
+ self.data_table = MDDataTable(
|
|
|
|
+ size_hint=(1, 1),
|
|
|
|
+ use_pagination=True,
|
|
|
|
+ rows_num=10,
|
|
|
|
+ column_data=[
|
|
|
|
+ ("Название", dp(30)),
|
|
|
|
+ ("Год", dp(15)),
|
|
|
|
+ ("Цена", dp(15)),
|
|
|
|
+ ("Цвет", dp(15)),
|
|
|
|
+ ("Повреждения", dp(25)),
|
|
|
|
+ ("Дата СТО", dp(30))
|
|
|
|
+ ],
|
|
|
|
+ # Инициализируем row_data пустым списком, данные будут загружены из JSON
|
|
|
|
+ row_data=[]
|
|
|
|
+ )
|
|
|
|
+ self.scrollview.add_widget(self.data_table)
|
|
|
|
+
|
|
|
|
+ # Загрузка данных из JSON файла
|
|
|
|
+ self.load_data_from_json('package.json')
|
|
|
|
+
|
|
|
|
+ def load_data_from_json(self, json_path):
|
|
|
|
+ # Загрузка данных из JSON файла и обновление таблицы
|
|
|
|
+ with open(json_path, 'r', encoding='utf-8') as json_file:
|
|
|
|
+ data = json.load(json_file)
|
|
|
|
+ # Предполагаем, что данные в JSON файле имеют формат списка словарей
|
|
|
|
+ row_data = [(d['Название'], d['Год'], d['Цена'], d['Цвет'], d['Повреждения'], d['Дата СТО']) for d in data]
|
|
|
|
+ self.data_table.row_data = row_data
|
|
|
|
+ self.original_row_data = row_data
|
|
|
|
+ self.original_row_data = self.data_table.row_data
|
|
|
|
+```
|
|
|
|
+
|
|
|
|
+**json файл:**
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+[
|
|
|
|
+ {
|
|
|
|
+ "Название": "Toyota Trueno AE86",
|
|
|
|
+ "Год": 1995,
|
|
|
|
+ "Цена": 15000,
|
|
|
|
+ "Цвет": "white",
|
|
|
|
+ "Повреждения": "True",
|
|
|
|
+ "Дата СТО": "19/04/2024",
|
|
|
|
+ },
|
|
|
|
+]
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Строка поиска и кнопки фильтрации**
|
|
|
|
+```
|
|
|
|
+# Панель поиска и фильтрации
|
|
|
|
+ search_filter_bar = BoxLayout(size_hint_y=None, height=dp(40), spacing=10) # Создание бокслояута для панели поиска и фильтрации
|
|
|
|
+ self.add_widget(search_filter_bar) # Добавление панели в основной бокслояут
|
|
|
|
+
|
|
|
|
+ # Строка поиска
|
|
|
|
+ self.search_field = MDTextField( # Создание строки ввода
|
|
|
|
+ hint_text="Search...", # Текст подсказки
|
|
|
|
+ size_hint_x=0.7 # Относительный размер строки ввода по горизонтали
|
|
|
|
+ )
|
|
|
|
+ self.search_field.bind(text=self.filter_table) # Привязка функции filter_table к изменению текста в строке поиска
|
|
|
|
+ search_filter_bar.add_widget(self.search_field) # Добавление строки поиска на панель
|
|
|
|
+
|
|
|
|
+ # Кнопки фильтрации
|
|
|
|
+ self.asc_button = MDIconButton(icon="arrow-up-drop-circle") # Создание кнопки с иконкой для сортировки по возрастанию
|
|
|
|
+ self.asc_button.bind(on_release=self.sort_ascending) # Привязка функции sort_ascending к нажатию на кнопку
|
|
|
|
+ search_filter_bar.add_widget(self.asc_button) # Добавление кнопки на панель
|
|
|
|
+
|
|
|
|
+ self.desc_button = MDIconButton(icon="arrow-down-drop-circle") # Создание кнопки с иконкой для сортировки по убыванию
|
|
|
|
+ self.desc_button.bind(on_release=self.sort_descending) # Привязка функции sort_descending к нажатию на кнопку
|
|
|
|
+ search_filter_bar.add_widget(self.desc_button) # Добавление кнопки на панель
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Выпадающий список с фильтрацией**
|
|
|
|
+```
|
|
|
|
+# Выпадающий список для выбора машины
|
|
|
|
+ self.spinner_car = Spinner( # Создание выпадающего списка для выбора машины
|
|
|
|
+ text='Выберите машину', # Начальный текст
|
|
|
|
+ values=('Toyota Trueno AE86', 'Toyota Supra A80', 'Nissan Skyline R34', 'Nissan Silvia S15', 'Toyota Camry 3.5', 'Audi RS 6', 'Трактор LOVOL TE354 HT', 'BMW M5 F90', 'BMW E36', 'Daewoo Matiz'), # Список значений
|
|
|
|
+ size_hint=(None, None), # Отключение автоматического расчета размера
|
|
|
|
+ size=(dp(200), dp(44)), # Фиксированный размер
|
|
|
|
+ pos_hint={'center_x': 0.5} # Позиционирование по центру по горизонтали
|
|
|
|
+ )
|
|
|
|
+ self.spinner_car.bind(text=self.filter_table_by_name) # Привязка функции filter_table_by_name к изменению значения в списке
|
|
|
|
+ search_filter_bar.add_widget(self.spinner_car) # Добавление списка на панель
|
|
|
|
+
|
|
|
|
+ # Выпадающий список для выбора ценового диапазона
|
|
|
|
+ self.spinner_price = Spinner( # Создание выпадающего списка для выбора ценового диапазона
|
|
|
|
+ text='Выберите цену', # Начальный текст
|
|
|
|
+ values=('10000 - 20000', '21000 - 30000', '31000 - 70000'), # Список значений
|
|
|
|
+ size_hint=(None, None), # Отключение автоматического расчета размера
|
|
|
|
+ size=(dp(200), dp(44)), # Фиксированный размер
|
|
|
|
+ pos_hint={'center_x': 0.5} # Позиционирование по центру по горизонтали
|
|
|
|
+ )
|
|
|
|
+ self.spinner_price.bind(text=self.filter_table_by_price) # Привязка функции filter_table_by_price к изменению значения в списке
|
|
|
|
+ search_filter_bar.add_widget(self.spinner_price) # Добавление списка на панель
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**Методы для всех фильтрациий и поисков (писать в конец кода)**
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+ def filter_table(self, instance, text):
|
|
|
|
+ # Фильтрация таблицы по поиску
|
|
|
|
+ filtered_data = []
|
|
|
|
+ for row in self.original_row_data:
|
|
|
|
+ if text.lower() in row[0].lower():
|
|
|
|
+ filtered_data.append(row)
|
|
|
|
+ self.data_table.row_data = filtered_data
|
|
|
|
+
|
|
|
|
+ def filter_table_by_name(self, spinner, text):
|
|
|
|
+ # Фильтрация таблицы по названию машины
|
|
|
|
+ if text != 'Выберите машину':
|
|
|
|
+ filtered_data = [row for row in self.original_row_data if row[0] == text]
|
|
|
|
+ self.data_table.row_data = filtered_data
|
|
|
|
+
|
|
|
|
+ def filter_table_by_price(self, spinner, text):
|
|
|
|
+ # Фильтрация таблицы по ценовому диапазону
|
|
|
|
+ if text != 'Выберите цену':
|
|
|
|
+ price_range = text.split(' - ')
|
|
|
|
+ min_price, max_price = int(price_range[0]), int(price_range[1])
|
|
|
|
+ filtered_data = [row for row in self.original_row_data if min_price <= int(row[2]) <= max_price]
|
|
|
|
+ self.data_table.row_data = filtered_data
|
|
|
|
+
|
|
|
|
+ def sort_ascending(self, instance):
|
|
|
|
+ # Сортировка таблицы по цене в порядке возрастания
|
|
|
|
+ self.data_table.row_data = sorted(self.original_row_data, key=lambda row: int(row[2]))
|
|
|
|
+
|
|
|
|
+ def sort_descending(self, instance):
|
|
|
|
+ # Сортировка таблицы по цене в порядке убывания
|
|
|
|
+ self.data_table.row_data = sorted(self.original_row_data, key=lambda row: int(row[2]), reverse=True)
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+Метод `filter_table(self, instance, text):`**
|
|
|
|
+
|
|
|
|
+ - `filtered_data = []`: создаёт пустой список для хранения отфильтрованных данных.
|
|
|
|
+ - Цикл `for row in self.original_row_data:`:
|
|
|
|
+ - Проходит по каждой строке `row` в исходных данных.
|
|
|
|
+ - `if text.lower() in row[0].lower():`:
|
|
|
|
+ - Проверяет, содержится ли текст поиска (`text`) в названии автомобиля (`row[0]`), игнорируя регистр.
|
|
|
|
+ - Если содержится, добавляет строку в `filtered_data`.
|
|
|
|
+ - `self.data_table.row_data = filtered_data`: - Обновляет данные в таблице, отображая только отфильтрованные строки.
|
|
|
|
+
|
|
|
|
+Метод `filter_table_by_name(self, spinner, text):` - Фильтрует таблицу по названию машины, выбранному в выпадающем списке.
|
|
|
|
+
|
|
|
|
+ - `if text != 'Выберите машину':`: проверяет, выбрана ли какая-то машина.
|
|
|
|
+
|
|
|
|
+ - `filtered_data = [row for row in self.original_row_data if row[0] == text]`: - Создаёт список, содержащий только строки, где название машины совпадает с выбранным.
|
|
|
|
+
|
|
|
|
+ - `self.data_table.row_data = filtered_data`: - Обновляет таблицу отфильтрованными данными.
|
|
|
|
+
|
|
|
|
+Метод `filter_table_by_price(self, spinner, text):` - Фильтрует таблицу по ценовому диапазону, выбранному в выпадающем списке.
|
|
|
|
+
|
|
|
|
+ - `if text != 'Выберите цену':`: проверяет, выбран ли ценовой диапазон.
|
|
|
|
+
|
|
|
|
+ - `price_range = text.split(' - ')`: разделяет текст диапазона на минимальную и максимальную цену.
|
|
|
|
+
|
|
|
|
+ - `min_price, max_price = int(price_range[0]), int(price_range[1])`: преобразует цены в числа.
|
|
|
|
+
|
|
|
|
+ - `filtered_data = [row for row in self.original_row_data if min_price <= int(row[2]) <= max_price]`:- Создаёт список, содержащий строки, где цена находится в заданном диапазоне.
|
|
|
|
+
|
|
|
|
+ - `self.data_table.row_data = filtered_data`:- Обновляет таблицу отфильтрованными данными.
|
|
|
|
+
|
|
|
|
+Метод `sort_ascending(self, instance):` - Сортирует таблицу по цене в порядке возрастания.
|
|
|
|
+ - `self.data_table.row_data = sorted(self.original_row_data, key=lambda row: int(row[2])):`: - Сортирует строки
|
|
|
|
+
|
|
|
|
+ - `self.original_row_data` по цене (`row[2]`), преобразуя её в число.
|
|
|
|
+
|
|
|
|
+Метод `sort_descending(self, instance):` - Сортирует таблицу по цене в порядке убывания.
|
|
|
|
+
|
|
|
|
+- `self.data_table.row_data = sorted(self.original_row_data, key=lambda row: int(row[2]), reverse=True):`: - Сортирует строки
|
|
|
|
+
|
|
|
|
+- `self.original_row_data` по цене (`row[2]`), преобразуя её в число, с обратным порядком (`reverse=True`).
|
|
|
|
+
|
|
|
|
+***
|
|
|
|
+
|
|
|
|
+**Замена таблицы на разметку с изображениями и текстом**
|
|
|
|
+```
|
|
|
|
+self.spinner_price.bind(text=self.filter_items)
|
|
|
|
+ search_filter_bar.add_widget(self.spinner_price)
|
|
|
|
+
|
|
|
|
+ # Прокрутка для таблицы
|
|
|
|
+ self.scrollview = ScrollView()
|
|
|
|
+ self.add_widget(self.scrollview)
|
|
|
|
+
|
|
|
|
+ # Сетчатая разметка для хранения элементов автомобиля
|
|
|
|
+ self.car_grid = GridLayout(cols=1, spacing=10, size_hint_y=None)
|
|
|
|
+ self.car_grid.bind(minimum_height=self.car_grid.setter('height'))
|
|
|
|
+ self.scrollview.add_widget(self.car_grid)
|
|
|
|
+
|
|
|
|
+ # Загрузка данных из JSON
|
|
|
|
+ self.car_data = self.load_data_from_json('package.json')
|
|
|
|
+ self.original_car_data = self.car_data.copy()
|
|
|
|
+ self.populate_car_grid()
|
|
|
|
+
|
|
|
|
+ self.spinner_car.values = list({car['Название'] for car in self.car_data})
|
|
|
|
+
|
|
|
|
+ def load_data_from_json(self, filename):
|
|
|
|
+ with open(filename, 'r', encoding='utf-8') as f:
|
|
|
|
+ data = json.load(f)
|
|
|
|
+ return data
|
|
|
|
+
|
|
|
|
+ def populate_car_grid(self):
|
|
|
|
+ # Заполнение сетки виджетов
|
|
|
|
+ self.car_grid.clear_widgets()
|
|
|
|
+ for car in self.car_data:
|
|
|
|
+ car_item = CarItem(car)
|
|
|
|
+ self.car_grid.add_widget(car_item)
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+**`self.spinner_price.bind(text=self.filter_items)`:**
|
|
|
|
+ - Эта строка связывает *изменение текста* в выпадающем списке `self.spinner_price` с методом `self.filter_items`.
|
|
|
|
+ - То есть, когда пользователь выбирает новое значение в `self.spinner_price`, Kivy автоматически вызывает `self.filter_items`.
|
|
|
|
+
|
|
|
|
+**`search_filter_bar.add_widget(self.spinner_price)`:**
|
|
|
|
+ - Добавляет выпадающий список `self.spinner_price` в контейнер `search_filter_bar`.
|
|
|
|
+ - `search_filter_bar` - это, скорее всего, `BoxLayout`, или другой контейнер, который хранит элементы панели поиска и фильтрации.
|
|
|
|
+
|
|
|
|
+**`self.scrollview = ScrollView()`:**
|
|
|
|
+ - Создаёт экземпляр класса `ScrollView`.
|
|
|
|
+ - `ScrollView` - это контейнер, который позволяет прокручивать содержимое, если оно не помещается в видимой области.
|
|
|
|
+
|
|
|
|
+**`self.add_widget(self.scrollview)`:**
|
|
|
|
+ - Добавляет `self.scrollview` в главный контейнер экрана.
|
|
|
|
+ - То есть, теперь `self.scrollview` будет отображаться на экране, и все его дочерние элементы тоже.
|
|
|
|
+
|
|
|
|
+**`self.car_grid = GridLayout(cols=1, spacing=10, size_hint_y=None)`:**
|
|
|
|
+ - Создаёт экземпляр класса `GridLayout`.
|
|
|
|
+ - `cols=1`: сетка будет иметь одну колонку.
|
|
|
|
+ - `spacing=10`: расстояние между элементами сетки будет 10 пикселей.
|
|
|
|
+ - `size_hint_y=None`: сетка не будет масштабироваться по вертикали, её высота будет определяться содержимым.
|
|
|
|
+
|
|
|
|
+**`self.car_grid.bind(minimum_height=self.car_grid.setter('height'))`:**
|
|
|
|
+ - Эта строка гарантирует, что высота сетки `self.car_grid` будет всегда достаточной для отображения всех элементов.
|
|
|
|
+
|
|
|
|
+**`self.scrollview.add_widget(self.car_grid)`:**
|
|
|
|
+ - Добавляет сетку `self.car_grid` в `self.scrollview`.
|
|
|
|
+ - Теперь содержимое `self.car_grid` можно будет прокручивать, если оно не помещается в видимой области.
|
|
|
|
+
|
|
|
|
+**`self.car_data = self.load_data_from_json('package.json')`:**
|
|
|
|
+ - Вызывает метод `self.load_data_from_json`, передавая ему имя файла `'package.json'`.
|
|
|
|
+ - Метод загружает данные из JSON файла и возвращает их.
|
|
|
|
+ - Результат сохраняется в `self.car_data`.
|
|
|
|
+
|
|
|
|
+**`self.original_car_data = self.car_data.copy()`:**
|
|
|
|
+ - Создает копию данных из `self.car_data` и сохраняет ее в `self.original_car_data`.
|
|
|
|
+ - Это делается для того, чтобы можно было фильтровать и сортировать данные, не изменяя исходный набор.
|
|
|
|
+**`self.populate_car_grid()`:**
|
|
|
|
+ - Вызывает метод `self.populate_car_grid`, который отвечает за заполнение сетки `self.car_grid` виджетами, представляющими автомобили.
|
|
|
|
+ - Предполагается, что у вас есть отдельный класс (например, `CarItem`) для создания виджетов, отображающих информацию об автомобиле.
|
|
|
|
+
|
|
|
|
+**`self.spinner_car.values = list({car['Название'] for car in self.car_data})`:**
|
|
|
|
+ - Эта строка задает значения для выпадающего списка `self.spinner_car`, который, вероятно, используется для фильтрации по названию машины.
|
|
|
|
+ - `{car['Название'] for car in self.car_data}`: создает множество уникальных названий машин из `self.car_data`.
|
|
|
|
+ - `list(...)`: преобразует множество в список, так как `spinner.values` ожидает список значений.
|
|
|
|
+***
|
|
|
|
+`json file`
|
|
|
|
+```
|
|
|
|
+[
|
|
|
|
+ {
|
|
|
|
+ "Название": "Toyota Trueno AE86",
|
|
|
|
+ "Год": 1995,
|
|
|
|
+ "Цена": 15000,
|
|
|
|
+ "Цвет": "white",
|
|
|
|
+ "Повреждения": "True",
|
|
|
|
+ "Дата СТО": "19/04/2024",
|
|
|
|
+ "image": "tru.jpg"
|
|
|
|
+ },
|
|
|
|
+]
|
|
|
|
+```
|
|
|
|
+***
|
|
|
|
+
|
|
|
|
+**все импорты, которые нам понадобились:**
|
|
|
|
+
|
|
|
|
+```
|
|
|
|
+from kivymd.app import MDApp # Импорт главного окна приложения
|
|
|
|
+from kivy.uix.boxlayout import BoxLayout # Импорт бокслояута для добавления в него виджетов
|
|
|
|
+from kivy.uix.gridlayout import GridLayout # Импорт гридлояута для добавления элементов в виде сетки
|
|
|
|
+from kivy.uix.spinner import Spinner # Импорт для выпадающего списка
|
|
|
|
+from kivymd.uix.textfield import MDTextField # Импорт для таблицы
|
|
|
|
+from kivymd.uix.button import MDIconButton # Импорт для иконок кнопок
|
|
|
|
+from kivy.uix.image import Image # Импорт для изменения размера
|
|
|
|
+from kivy.uix.label import Label # Импорт Label для отображения текста
|
|
|
|
+from kivy.uix.scrollview import ScrollView # Импорт для прокрутки таблицы
|
|
|
|
+from kivy.metrics import dp # Импорт для изменения размера
|
|
|
|
+from kivy.utils import get_color_from_hex # Импорт для использования шестнадцатеричных цветовых кодов
|
|
|
|
+
|
|
|
|
+import json
|
|
|
|
+```
|