Merge branch 'dev'
3
.gitignore
vendored
@@ -127,3 +127,6 @@ dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# Kivy
|
||||
.buildozer/
|
||||
|
||||
BIN
MockUps/Canva/Version 1/1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
MockUps/Canva/Version 1/2.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
MockUps/Canva/Version 1/3.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
MockUps/Canva/Version 1/4.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
MockUps/Canva/Version 2 (finally)/1_1.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
MockUps/Canva/Version 2 (finally)/2_1.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
MockUps/Canva/Version 2 (finally)/3_1.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
MockUps/Canva/Version 2 (finally)/4_1.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
1
MockUps/DrawIO/mock5.drawio
Normal file
@@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2023-03-06T11:09:54.626Z" agent="5.0 (Windows)" etag="DccbIbGf31_jB3hpeEkb" version="21.0.2" type="device"><diagram name="Seite-1" id="MAx1qc_yxkcqeCQ3BJs1">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</diagram></mxfile>
|
||||
BIN
MockUps/DrawIO/mock5.drawio.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
12
requirements.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
certifi==2022.12.7
|
||||
charset-normalizer==3.1.0
|
||||
docutils==0.19
|
||||
idna==3.4
|
||||
Kivy==2.1.0
|
||||
Kivy-Garden==0.1.5
|
||||
kivymd==1.1.1
|
||||
Pillow==9.4.0
|
||||
Pygments==2.14.0
|
||||
requests==2.28.2
|
||||
urllib3==1.26.15
|
||||
watchdog
|
||||
1
sources.md
Normal file
@@ -0,0 +1 @@
|
||||
- icons von: [Google fonts](https://fonts.google.com/icons?selected=Material+Symbols+Rounded:shopping_bag:FILL@0;wght@400;GRAD@0;opsz@48&icon.style=Rounded)
|
||||
130
src/main.py
Normal file
@@ -0,0 +1,130 @@
|
||||
from os import name
|
||||
from kivy.app import App
|
||||
from kivy.properties import ListProperty, StringProperty, BooleanProperty, ObjectProperty
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.button import Label
|
||||
from kivy.metrics import dp
|
||||
from kivy.uix.screenmanager import Screen, ScreenManager
|
||||
from kivy.core.window import Window
|
||||
|
||||
from kivymd.app import MDApp
|
||||
from kivymd.uix.button import MDFlatButton
|
||||
from kivymd.uix.card.card import MDBoxLayout
|
||||
from kivymd.uix.dialog import MDDialog
|
||||
from kivymd.uix.menu import MDDropdownMenu
|
||||
from kivymd.uix.list import OneLineAvatarIconListItem
|
||||
|
||||
from setting_widgets.setting_fields import SettingTextField, SettingToggleField, SettingDropDownField
|
||||
|
||||
|
||||
LANGUAGES = { "DE": "Deutsch", "EN": "English", "FR": "Francais" }
|
||||
|
||||
Window.size = (400, 800)
|
||||
|
||||
class ShoppingEntry(OneLineAvatarIconListItem):
|
||||
def __init__(self, text, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.text = text
|
||||
self.is_checked = False
|
||||
|
||||
def delete(self, shopping_entry):
|
||||
self.parent.remove_widget(shopping_entry)
|
||||
|
||||
class AddDialog(MDBoxLayout):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
class ShoppingEntryScreen(Screen):
|
||||
add_dialog = None
|
||||
|
||||
def open_add_popup(self):
|
||||
if self.add_dialog:
|
||||
return
|
||||
|
||||
# SPRACHE
|
||||
buttons = [
|
||||
MDFlatButton(text='Abbrechen', on_release=self.close_add_popup),
|
||||
MDFlatButton(text='Bestätigen', on_release=lambda args: self.add_shopping_entry("Text"))
|
||||
]
|
||||
# SPRACHE
|
||||
self.add_dialog = MDDialog(
|
||||
title='Eintrag hinzufügen',
|
||||
type='custom',
|
||||
content_cls=AddDialog(),
|
||||
buttons=buttons
|
||||
)
|
||||
|
||||
self.add_dialog.open()
|
||||
|
||||
# *args ist noetig, da weitere Parameter mitgegeben werden, die aber nicht genutzt werden
|
||||
def close_add_popup(self, *args):
|
||||
if not self.add_dialog:
|
||||
return
|
||||
|
||||
self.add_dialog.dismiss()
|
||||
self.add_dialog = None
|
||||
|
||||
|
||||
def add_shopping_entry(self, text):
|
||||
self.ids['shopping_list'].add_widget(ShoppingEntry(text=text))
|
||||
self.close_add_popup()
|
||||
|
||||
def delete_entry(self):
|
||||
pass
|
||||
|
||||
def navigate_to_settings(self):
|
||||
self.manager.transition.direction = 'left'
|
||||
self.manager.current = 'settings'
|
||||
|
||||
class SettingsScreen(Screen):
|
||||
#languages = ListProperty(["Deutsch", "English", "Francais"])
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
menu_items = [
|
||||
{
|
||||
"viewclass": "OneLineListItem",
|
||||
"text": f"{LANGUAGES.get(language_key)}",
|
||||
"height": dp(56),
|
||||
"on_release": lambda x=f"{LANGUAGES.get(language_key)}": self.set_item(language_key),
|
||||
} for language_key in LANGUAGES.keys()
|
||||
]
|
||||
|
||||
self.menu = MDDropdownMenu(
|
||||
caller=self.ids.language_drop_down,
|
||||
items=menu_items,
|
||||
position="center",
|
||||
width_mult=4,
|
||||
)
|
||||
|
||||
self.menu.bind()
|
||||
|
||||
def navigate_to_shopping_list(self):
|
||||
self.manager.transition.direction = 'right'
|
||||
self.manager.current = 'shopping'
|
||||
|
||||
def show_languages_menu(self):
|
||||
self.menu.open()
|
||||
|
||||
def set_item(self, language_key):
|
||||
language = LANGUAGES.get(language_key)
|
||||
self.ids.language_drop_down.set_item(language)
|
||||
self.menu.dismiss()
|
||||
|
||||
class ShoppingListApp(MDApp):
|
||||
def build(self):
|
||||
self.theme_cls.primary_palette = "Teal"
|
||||
self.theme_cls.theme_style = "Light"
|
||||
self.title = 'Shopping List App'
|
||||
|
||||
sm = ScreenManager()
|
||||
sm.add_widget(ShoppingEntryScreen(name='shopping'))
|
||||
sm.add_widget(SettingsScreen(name='settings'))
|
||||
|
||||
return sm
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = ShoppingListApp()
|
||||
app.run()
|
||||
BIN
src/res/add_circle.png
Normal file
|
After Width: | Height: | Size: 990 B |
BIN
src/res/arrow_back.png
Normal file
|
After Width: | Height: | Size: 389 B |
BIN
src/res/delete.png
Normal file
|
After Width: | Height: | Size: 472 B |
BIN
src/res/expand_more.png
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
src/res/filter_list.png
Normal file
|
After Width: | Height: | Size: 345 B |
BIN
src/res/settings.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/res/shopping_bag.png
Normal file
|
After Width: | Height: | Size: 498 B |
3
src/setting_widgets/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .setting_fields import SettingTextField
|
||||
from .setting_fields import SettingToggleField
|
||||
from .setting_fields import SettingDropDownField
|
||||
20
src/setting_widgets/setting_fields.kv
Normal file
@@ -0,0 +1,20 @@
|
||||
<SettingTextField>:
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
RightTextField:
|
||||
id: setting_right_text_field
|
||||
|
||||
<SettingToggleField>:
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
RightToggleField:
|
||||
id: setting_right_toggle_field
|
||||
|
||||
<SettingDropDownField>:
|
||||
divider: None
|
||||
_no_ripple_effect: True
|
||||
|
||||
RightDropDownField:
|
||||
id: setting_right_dropdown_field
|
||||
31
src/setting_widgets/setting_fields.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from kivymd.uix.dropdownitem import MDDropDownItem
|
||||
from kivymd.uix.list import BaseListItem, OneLineRightIconListItem, IRightBodyTouch
|
||||
from kivymd.uix.selectioncontrol.selectioncontrol import MDSwitch
|
||||
from kivymd.uix.textfield import MDTextField
|
||||
from kivy.lang import Builder
|
||||
|
||||
Builder.load_file("setting_widgets\setting_fields.kv")
|
||||
|
||||
# ===== TextField =====
|
||||
|
||||
class SettingTextField(OneLineRightIconListItem):
|
||||
"""DOCS"""
|
||||
|
||||
class RightTextField(IRightBodyTouch, MDTextField):
|
||||
"""DOCS"""
|
||||
|
||||
# ===== ToggleField =====
|
||||
|
||||
class SettingToggleField(OneLineRightIconListItem):
|
||||
"""DOCS"""
|
||||
|
||||
class RightToggleField(IRightBodyTouch, MDSwitch):
|
||||
"""DOCS"""
|
||||
|
||||
# ===== DropDownField =====
|
||||
|
||||
class SettingDropDownField(OneLineRightIconListItem):
|
||||
"""DOCS"""
|
||||
|
||||
class RightDropDownField(IRightBodyTouch, MDDropDownItem):
|
||||
"""DOCS"""
|
||||
113
src/shoppinglist.kv
Normal file
@@ -0,0 +1,113 @@
|
||||
<ShoppingEntry>:
|
||||
id: shopping_entry
|
||||
markup: True
|
||||
|
||||
ImageLeftWidget:
|
||||
MDCheckbox:
|
||||
id: shopping_entry_check
|
||||
# bind to property
|
||||
|
||||
IconRightWidget:
|
||||
icon: 'res/delete.png'
|
||||
on_release: root.delete(shopping_entry)
|
||||
|
||||
<AddDialog>:
|
||||
size_hint: 1, None
|
||||
height: '40dp'
|
||||
orientation: 'vertical'
|
||||
spacing: '5dp'
|
||||
|
||||
MDTextField:
|
||||
id: shopping_entry_text
|
||||
pos_hint: { 'center_y': 0.4 }
|
||||
# SPRACHE
|
||||
hint_text: 'Neuer Eintrag'
|
||||
|
||||
<ShoppingEntryScreen>:
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
pos_hint: { 'center_x': 0.5, 'center_y': 0.5 }
|
||||
|
||||
MDTopAppBar:
|
||||
#SPRACHE
|
||||
title: 'Shopping-List-App'
|
||||
md_bg_color: app.theme_cls.primary_color
|
||||
specific_text_color: '#000000'
|
||||
right_action_items: [['res/settings.png', lambda x: root.navigate_to_settings()]]
|
||||
|
||||
ScrollView:
|
||||
size_hint: 0.85, 0.85
|
||||
pos_hint: { 'center_y': 1, 'center_x': 0.5 }
|
||||
MDList:
|
||||
id: shopping_list
|
||||
|
||||
MDFloatingActionButton:
|
||||
icon: 'plus'
|
||||
elevation_normal: 5
|
||||
# bottom align is complicated
|
||||
pos_hint: { 'top': (self.height/root.height)*1.5, 'right':0.95 }
|
||||
on_release: root.open_add_popup()
|
||||
|
||||
<SettingsScreen>:
|
||||
MDBoxLayout:
|
||||
orientation: 'vertical'
|
||||
|
||||
MDTopAppBar:
|
||||
#SPRACHE
|
||||
title: 'Shopping-List-App'
|
||||
md_bg_color: app.theme_cls.primary_color
|
||||
specific_text_color: '#000000'
|
||||
left_action_items: [['res/arrow_back.png', lambda x: root.navigate_to_shopping_list()]]
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'horizontal'
|
||||
pos_hint: { 'center_x': 0.5 }
|
||||
size_hint: (0.7, 0.25)
|
||||
# SPRACHE
|
||||
MDLabel:
|
||||
text: 'Sprache'
|
||||
MDDropDownItem:
|
||||
id: language_drop_down
|
||||
text: 'Deutsch'
|
||||
on_release: root.show_languages_menu()
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'horizontal'
|
||||
pos_hint: { 'center_x': 0.5 }
|
||||
size_hint: (0.7, 0.25)
|
||||
# SPRACHE
|
||||
SettingToggleField:
|
||||
text: 'Theme'
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'horizontal'
|
||||
pos_hint: { 'center_x': 0.5 }
|
||||
size_hint: (0.7, 0.25)
|
||||
# SPRACHE
|
||||
SettingTextField:
|
||||
text: 'MQTT-Server'
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'horizontal'
|
||||
pos_hint: { 'center_x': 0.5 }
|
||||
size_hint: (0.7, 0.25)
|
||||
# SPRACHE
|
||||
SettingTextField:
|
||||
text: 'MQTT-Topic'
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'horizontal'
|
||||
pos_hint: { 'center_x': 0.5 }
|
||||
size_hint: (0.7, 0.25)
|
||||
# SPRACHE
|
||||
SettingTextField:
|
||||
text: 'MQTT-Benutzername'
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: 'horizontal'
|
||||
pos_hint: { 'center_x': 0.5 }
|
||||
size_hint: (0.7, 0.25)
|
||||
# SPRACHE
|
||||
SettingTextField:
|
||||
text: 'MQTT-Passwort'
|
||||
|
||||