from kivy.uix.screenmanager import Screen from kivy.uix.boxlayout import BoxLayout from kivy.uix.label import Label from kivy.metrics import dp from kivy.logger import Logger from kivy.app import App from kivy.clock import Clock from ui_utils import _popup_info import numberpad from kivy.uix.anchorlayout import AnchorLayout from kivy.core.window import Window MAX_LOGIN_FAILS = 3 # nebo treba 5 class LoginUserScreen(Screen): def __init__(self, controller, on_success, **kwargs): super().__init__(**kwargs) self.fail_count = 0 self.controller = controller self.on_success = on_success root = BoxLayout(orientation="vertical") self.lbl_info = Label( text="Zadej PIN číšníka", size_hint_y=None, height=40, ) self.lbl_error = Label( text="", color=(1, 0, 0, 1), size_hint_y=None, height=30, ) root.add_widget(self.lbl_info) root.add_widget(self.lbl_error) self.center_layout = AnchorLayout(anchor_x="center", anchor_y="center") root.add_widget(self.center_layout) self.pad = None self._keyboard_bound = False self.add_widget(root) def _on_key_down(self, window, key, scancode, codepoint, modifiers): if self.manager and self.manager.current != self.name: return False if self.pad: if key == 27: self.pad._cancel() return True # 🔥 zabráni zatvoreniu appky if key in range(48, 58): # top row self.pad._press(chr(key)) return True elif key in range(256, 266): # numpad self.pad._press(str(key - 256)) return True elif chr(key)=='*' or key == 268: self.pad._press("*") return True elif chr(key)=='.' or key == 266: self.pad._press(".") return True elif key > 300: print("pass") pass return True elif codepoint and (codepoint.isalpha() or codepoint.isdigit()): self.pad._press(codepoint) return True elif key == 8: self.pad._press('⌫') return True elif key in (13, 40, 10, 271): # Enter / Numpad Enter self.pad._accept() return True else: return False else: return False def on_enter(self): self._bind_keyboard() self.lbl_error.text = "" if self.pad: self.center_layout.remove_widget(self.pad) self.pad = numberpad.NumberPad( mode="code", max_len=10, on_accept=self._accept, on_cancel=self._cancel, ) self.pad.size_hint = (None, None) self.pad.size = (dp(300), dp(400)) self.center_layout.add_widget(self.pad) def on_leave(self): self._unbind_keyboard() if self.pad: self.center_layout.remove_widget(self.pad) self.pad = None def _bind_keyboard(self): if self._keyboard_bound: return Window.unbind(on_key_down=self._on_key_down) Window.bind(on_key_down=self._on_key_down) self._keyboard_bound = True def _unbind_keyboard(self): Window.unbind(on_key_down=self._on_key_down) self._keyboard_bound = False def _accept(self, pin: str): try: self.controller.login_user(pin) # úspěch → reset čítače self.fail_count = 0 Logger.info("LOGIN USER OK") self.on_success() except Exception as e: self.fail_count += 1 Logger.warning( f"LOGIN FAILED ({self.fail_count}): {type(e).__name__}: {e}" ) self.pad.clear() # příliš mnoho pokusů → konec aplikace if self.fail_count >= MAX_LOGIN_FAILS: Logger.error("MAX LOGIN FAILS REACHED") _popup_info("Chyba", "Příliš mnoho chybných pokusů.\nAplikace bude ukončena.") Clock.schedule_once(lambda *_: App.get_running_app().stop(), 2) def _cancel(self): # v POS obvykle znamená ukončení aplikace Logger.info("LOGIN CANCEL") App.get_running_app().stop()