-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
286 lines (243 loc) · 9.83 KB
/
main.py
File metadata and controls
286 lines (243 loc) · 9.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
from kivy.config import Config
Config.set('graphics', 'width', '360')
Config.set('graphics', 'height', '800')
import sqlite3
import os
from datetime import datetime
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import StringProperty, NumericProperty, BooleanProperty
from kivy.core.window import Window
from kivy.clock import Clock
from collections import OrderedDict
# -----------------------------
# Klasa do obsługi bazy danych SQLite
# -----------------------------
class MileageDB:
def __init__(self, db_path="mileage.db"):
self.conn = sqlite3.connect(db_path)
self.cursor = self.conn.cursor()
self._create_table()
def _create_table(self):
# Tworzy tabelę, jeśli nie istnieje: kolumny to miesiąc (klucz), przebieg, delegacje
self.cursor.execute("""
CREATE TABLE IF NOT EXISTS mileage (
month TEXT PRIMARY KEY,
mileage INTEGER,
delegations INTEGER
)
""")
self.conn.commit()
def save_month_data(self, month: str, mileage: int, delegations: int):
# Wstawia lub aktualizuje dane dla danego miesiąca
self.cursor.execute("""
INSERT OR REPLACE INTO mileage (month, mileage, delegations)
VALUES (?, ?, ?)
""", (month, mileage, delegations))
self.conn.commit()
def get_month_data(self, month: str):
# Zwraca dane (mileage, delegations) dla danego miesiąca jako krotkę
self.cursor.execute("SELECT mileage, delegations FROM mileage WHERE month = ?", (month,))
row = self.cursor.fetchone()
return row if row else (0, 0)
def get_all_months(self):
# Zwraca listę wszystkich miesięcy zapisanych w bazie
self.cursor.execute("SELECT month FROM mileage ORDER BY month DESC")
return [row[0] for row in self.cursor.fetchall()]
# -----------------------------
# Główna klasa layoutu aplikacji
# -----------------------------
class MileageLayout(BoxLayout):
# Właściwości dynamiczne – automatycznie aktualizują UI
current_month = StringProperty()
start = StringProperty("")
end = StringProperty("")
deleg = NumericProperty(0)
total = NumericProperty(0)
total_daily = NumericProperty(0)
d_counter = NumericProperty(0)
start_locked = BooleanProperty(False)
end_locked = BooleanProperty(False)
deleg_locked = BooleanProperty(False)
warning_text = StringProperty("")
selected_month_key = StringProperty(datetime.now().strftime("%Y-%m"))
selected_month_display = StringProperty(datetime.now().strftime("%B %Y"))
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.db = MileageDB()
self.current_month = datetime.now().strftime("%B %Y")
self.start = ""
self.end = ""
mileage, delegations = self.db.get_month_data(self.selected_month_key)
self.d_counter = delegations
self.update_total()
self.update_confirm_button_state()
def set_selected_month(self, month_key):
months_pl = {
'January': 'Styczeń',
'February': 'Luty',
'March': 'Marzec',
'April': 'Kwiecień',
'May': 'Maj',
'June': 'Czerwiec',
'July': 'Lipiec',
'August': 'Sierpień',
'September': 'Wrzesień',
'October': 'Październik',
'November': 'Listopad',
'December': 'Grudzień'
}
self.selected_month_key = month_key
dt = datetime.strptime(month_key, "%Y-%m")
month_en = dt.strftime("%B")
month_pl = months_pl.get(month_en, month_en)
self.selected_month_display = f"{month_pl} {dt.year}"
data = self.db.get_month_data(month_key)
if data:
self.start = str(data[0])
self.end = str(data[1])
self.d_counter = data[2]
self.update_total()
self.start_locked = True
self.update_confirm_button_state()
def update_confirm_button_state(self):
# 🔽 Tu dodajemy blok zarządzający przyciskiem zapisu
is_current = (self.selected_month_key == datetime.now().strftime("%Y-%m"))
self.set_editable_state(is_current)
confirm_button = self.ids.confirm_button
if is_current:
self.set_buttons_enabled(True)
confirm_button.text = "Zapisz i wyjdź"
else:
self.set_buttons_enabled(False)
confirm_button.text = "Edycja zablokowana"
def set_editable_state(self, editable):
# Funkcja zmienia stan pól w zależności od miesiąca
self.ids.start_input.disabled = not editable
self.ids.end_input.disabled = not editable
self.ids.deleg_input.disabled = not editable
def set_buttons_enabled(self, enabled: bool):
# Blokuje lub odblokowuje wszystkie przyciski w zależności od wybranego miesiąca
self.ids.start_button.disabled = not enabled
self.ids.end_button.disabled = not enabled
self.ids.deleg_button.disabled = not enabled
self.ids.confirm_button.disabled = not enabled
def show_month_selector(self):
# Pokazuje popup z listą zapisanych miesięcy
available_months = list(OrderedDict.fromkeys(self.db.get_all_months()))
layout = GridLayout(cols=1, spacing=10, size_hint_y=None)
layout.bind(minimum_height=layout.setter('height'))
for month in available_months:
btn = Button(text=month, size_hint_y=None, height=80)
btn.bind(on_release=lambda btn: self.select_month(btn.text))
layout.add_widget(btn)
scroll = ScrollView(size_hint=(1, 1))
scroll.add_widget(layout)
self.popup = Popup(title="Wybierz miesiąc", content=scroll,
size_hint=(0.8, 0.8), auto_dismiss=True)
self.popup.open()
def select_month(self, month_key):
# Wybiera miesiąc z popupu i aktualizuje dane
self.selected_month_key = month_key
data = self.db.get_month_data(month_key)
self.total = data[0]
self.d_counter = data[1]
try:
date_obj = datetime.strptime(month_key, "%Y-%m")
self.selected_month_display = date_obj.strftime("%B %Y")
self.popup.dismiss()
self.update_confirm_button_state()
except ValueError:
self.selected_month_display = month_key
def update_start(self, text):
# Aktualizuje pole "start" przebiegu
if text.isdigit() and text != self.start:
if not self.start_locked:
self.start = text[:7]
def update_end(self, text):
# Aktualizuje pole "end" przebiegu i oblicza przebieg
if self.start_locked and text.isdigit():
end_value = int(text[:7])
try:
start_value = int(self.start)
except ValueError:
return
if end_value > start_value:
self.end = str(end_value)
self.warning_text = ""
self.total_daily = end_value - start_value
db_mileage, _ = self.db.get_month_data(self.selected_month_key)
self.total = db_mileage + self.total_daily
else:
self.warning_text = "Wartość końcowa > wartość początkowa"
def update_deleg(self, text):
# Aktualizuje pole ilości delegacji
if not self.deleg_locked and text.isdigit():
self.deleg = int(text[:3])
def update_total(self):
# Aktualizuje przebieg całkowity
try:
db_mileage, _ = self.db.get_month_data(self.selected_month_key)
self.total = db_mileage + self.total_daily
except ValueError:
pass
def save(self):
# Zapisuje dane do bazy i zamyka aplikację po 5 sekundach
self.d_counter += self.deleg
self.db.save_month_data(self.selected_month_key, self.total, self.d_counter)
self.ids.confirm_button.disabled = True # blokuje przycisk od razu
Clock.schedule_once(self.closing, 5)
def lock_start(self):
# Blokuje pole początkowe
if self.start:
self.start_locked = True
self.update_total()
def lock_end(self):
# Blokuje pole końcowe
if self.end:
self.end_locked = True
self.update_total()
def lock_deleg(self):
# Blokuje pole delegacji
self.deleg_locked = True
def unlock_start(self):
# Odblokowuje pole początkowe
self.start_locked = False
def unlock_end(self):
# Odblokowuje pole końcowe
self.end_locked = False
def unlock_deleg(self):
# Odblokowuje pole delegacji
self.deleg_locked = False
def closing(self, *args):
# Zamyka okno aplikacji
Window.close()
# -----------------------------
# Główna klasa aplikacji
# -----------------------------
class MileageApp(App):
def build(self):
root = FloatLayout()
# ustal ścieżkę do tła w folderze assets/
background_path = os.path.join(os.path.dirname(__file__), 'assets', 'dusk2_cropped.jpg')
# ustawienie tła dla aplikacji. Obraz w tej samej lokacji co main.py
background = Image(
source=background_path,
allow_stretch=True,
keep_ratio=False,
size_hint=(1, 1),
pos_hint={'x': 0, 'y': 0}
)
root.add_widget(background)
content = MileageLayout()
root.add_widget(content)
return root
if __name__ == "__main__":
MileageApp().run()