BonPrinter v1.2.0
Thermal Printer tool
Loading...
Searching...
No Matches
app_data.py
Go to the documentation of this file.
1"""!
2********************************************************************************
3@file app_data.py
4@brief Data module (path related constants and functions)
5********************************************************************************
6"""
7
8import sys
9import os
10import logging
11import enum
12from typing import Any, Optional, TYPE_CHECKING
13from pathlib import Path
14import base64
15import socket
16import locale
17import copy
18import subprocess
19import platform
20import threading
21import serial # serial.Serial need this
22from serial.tools.list_ports import comports
23from serial.tools.list_ports_common import ListPortInfo
24if TYPE_CHECKING:
25 from Source.Controller.main_window import MainWindow
26
27if platform.system() == 'Windows':
28 import win32crypt
29
30from Source.version import __title__ # pylint: disable=wrong-import-position
31from Source.Util.gui_toolkit import SETTINGS, input_dialog, BYTE_ARRAY # pylint: disable=wrong-import-position
32
33log = logging.getLogger(__title__)
34
35
36def resource_path(s_relative_path: str) -> str:
37 """!
38 @brief Returns the absolute path to a resource given by a relative path depending on the environment (EXE or Python)
39 @param s_relative_path : the relative path to a file or directory
40 @return absolute path to the resource
41 """
42 if hasattr(sys, "_MEIPASS"): # check without function call for mypy
43 # PyInstaller creates a temp folder and stores path in _MEIPASS
44 s_base_path = sys._MEIPASS
45 else:
46 s_base_path = os.path.abspath("../")
47 s_resource_path = os.path.join(s_base_path, s_relative_path)
48 log.debug("Resource Path (relative %s): %s", s_relative_path, s_resource_path)
49 return s_resource_path
50
51
52def thread_loop(obj: Any, thread_name: str) -> None:
53 """!
54 @brief loop for threads.
55 @param obj : object of thread class
56 @param thread_name : thread name
57 """
58 threading.current_thread().name = thread_name
59 try:
60 while True:
61 obj.loop()
62 except Exception:
63 exc_type, exc_obj, exc_tb = sys.exc_info()
64 if exc_tb is not None:
65 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
66 lineno = exc_tb.tb_lineno
67 else:
68 fname = "None"
69 lineno = 0
70 s_err = f"Thread {thread_name}: {exc_type} {fname} {exc_obj} {lineno}"
71 log.error(s_err)
72 obj.ui.qt_exception_hook.exception_caught.emit(s_err)
73 sys.exit(1)
74
75
76def reset_config_dialog(ui: "MainWindow | None") -> tuple[bool, bool | None]:
77 """!
78 @brief Show reset configuration dialog. Enter reset PW to reset configuration.
79 @param ui : main window controller
80 @return status if PW is correct
81 """
82 l_reset_title = ["Reset", "Zurücksetzen"]
83 l_reset_text = ["Enter reset code to reset configuration.", "Reset-Code eingeben, um die Konfiguration zurückzusetzen."]
84 if ui:
85 reset_title = ui.model.c_language.get_language_text(l_reset_title)
86 reset_text = ui.model.c_language.get_language_text(l_reset_text)
87 else:
88 reset_title = l_reset_title[0]
89 reset_text = l_reset_text[0]
90 password, ok = input_dialog(ui, reset_title, reset_text, password_input=True, icon=ICON_APP)
91 b_reset = bool(ok and password.isdigit() and (base64.b64encode(password.encode("utf-8")) == RESET_CODE))
92 if b_reset:
94 return b_reset, ok
95
96
97def get_computer_name() -> str:
98 """!
99 @brief Get computer name.
100 @return computer name
101 """
102 try:
103 hostname = socket.gethostname()
104 except BaseException:
105 hostname = ""
106 return hostname
107
108
109def open_explorer(explorer_path: str, b_open_input: bool = False) -> None:
110 """!
111 @brief Open explorer.
112 @param explorer_path : path to open
113 @param b_open_input : open explorer into folder
114 """
115 s_open_folder = explorer_path.replace("/", "\\")
116 mode = "explorer " if b_open_input else r"explorer /select, "
117 with subprocess.Popen(mode + s_open_folder):
118 pass
119
120
121def check_serial_device(s_com_port: str) -> bool:
122 """!
123 @brief Check if serial device is present at this COM port
124 @param s_com_port : COM port to check
125 @return return display status: [True] serial device at this COM port available; [False] not available
126 """
127 b_present = False
128 try:
129 device = serial.Serial(s_com_port)
130 except BaseException:
131 b_present = False
132 else:
133 device.close()
134 b_present = True
135 return b_present
136
137
138def get_serial_ports() -> list[ListPortInfo]:
139 """!
140 @brief Check if serial device is present at this COM port
141 @return return list of present serial ports
142 """
143 l_present_ports = sorted(comports()) # list of objects with port class
144 return l_present_ports
145
146
147# Files and Paths
148# https://icons8.com/icons/fluency-systems-regular
149# https://compresspng.com/
150# https://ezgif.com/optimize
151# https://compress-image.net/compress-image-online/compress-ico
152# https://www.zamzar.com/compress-bmp/
153# General Icon and Images
154ICON_APP_PATH = "Resources/app.ico"
155ICON_APP_FAVICON_PATH = "Resources/favicon.ico"
156ICON_APP = resource_path(ICON_APP_PATH)
157ICON_LOCK_LIGHT = resource_path("Resources/Icon/lock_light.png")
158ICON_LOCK_DARK = resource_path("Resources/Icon/lock_dark.png")
159IMG_SPLASH = resource_path("Resources/splash.gif")
160# Menu Prints
161ICON_STATUS_LIGHT = resource_path("Resources/Icon/status_light.png")
162ICON_STATUS_DARK = resource_path("Resources/Icon/status_dark.png")
163ICON_OPEN_FOLDER_LIGHT = resource_path("Resources/Icon/open_folder_light.png")
164ICON_OPEN_FOLDER_DARK = resource_path("Resources/Icon/open_folder_dark.png")
165ICON_COMBINE_LIGHT = resource_path("Resources/Icon/combine_light.png")
166ICON_COMBINE_DARK = resource_path("Resources/Icon/combine_dark.png")
167ICON_PRINT_FILE_LIGHT = resource_path("Resources/Icon/print_file_light.png")
168ICON_PRINT_FILE_DARK = resource_path("Resources/Icon/print_file_dark.png")
169ICON_PRINT_PREVIEW_LIGHT = resource_path("Resources/Icon/print_preview_light.png")
170ICON_PRINT_PREVIEW_DARK = resource_path("Resources/Icon/print_preview_dark.png")
171ICON_REPORT_LIGHT = resource_path("Resources/Icon/report_light.png")
172ICON_REPORT_DARK = resource_path("Resources/Icon/report_dark.png")
173# Menu Configuration
174ICON_USER_LIGHT = resource_path("Resources/Icon/user_light.png")
175ICON_USER_DARK = resource_path("Resources/Icon/user_dark.png")
176ICON_USER_EDIT_LIGHT = resource_path("Resources/Icon/user_edit_light.png")
177ICON_USER_EDIT_DARK = resource_path("Resources/Icon/user_edit_dark.png")
178ICON_USER_IMPORT_LIGHT = resource_path("Resources/Icon/user_import_light.png")
179ICON_USER_IMPORT_DARK = resource_path("Resources/Icon/user_import_dark.png")
180ICON_USER_EXPORT_LIGHT = resource_path("Resources/Icon/user_export_light.png")
181ICON_USER_EXPORT_DARK = resource_path("Resources/Icon/user_export_dark.png")
182ICON_USER_RESET_LIGHT = resource_path("Resources/Icon/user_reset_light.png")
183ICON_USER_RESET_DARK = resource_path("Resources/Icon/user_reset_dark.png")
184ICON_ARTICLES_LIGHT = resource_path("Resources/Icon/articles_light.png")
185ICON_ARTICLES_DARK = resource_path("Resources/Icon/articles_dark.png")
186ICON_ARTICLES_EDIT_LIGHT = resource_path("Resources/Icon/articles_edit_light.png")
187ICON_ARTICLES_EDIT_DARK = resource_path("Resources/Icon/articles_edit_dark.png")
188ICON_ARTICLES_IMPORT_LIGHT = resource_path("Resources/Icon/articles_import_light.png")
189ICON_ARTICLES_IMPORT_DARK = resource_path("Resources/Icon/articles_import_dark.png")
190ICON_ARTICLES_EXPORT_LIGHT = resource_path("Resources/Icon/articles_export_light.png")
191ICON_ARTICLES_EXPORT_DARK = resource_path("Resources/Icon/articles_export_dark.png")
192ICON_ARTICLES_RESET_LIGHT = resource_path("Resources/Icon/articles_reset_light.png")
193ICON_ARTICLES_RESET_DARK = resource_path("Resources/Icon/articles_reset_dark.png")
194ICON_CHANGE_FOLDER_LIGHT = resource_path("Resources/Icon/change_folder_light.png")
195ICON_CHANGE_FOLDER_DARK = resource_path("Resources/Icon/change_folder_dark.png")
196ICON_PRINTER_LIGHT = resource_path("Resources/Icon/printer_light.png")
197ICON_PRINTER_DARK = resource_path("Resources/Icon/printer_dark.png")
198ICON_PAPER_LIGHT = resource_path("Resources/Icon/paper_light.png")
199ICON_PAPER_DARK = resource_path("Resources/Icon/paper_dark.png")
200# Menu Settings
201ICON_SOUND_LIGHT = resource_path("Resources/Icon/sound_light.png")
202ICON_SOUND_DARK = resource_path("Resources/Icon/sound_dark.png")
203ICON_SOUND_MUTE_LIGHT = resource_path("Resources/Icon/sound_mute_light.png")
204ICON_SOUND_MUTE_DARK = resource_path("Resources/Icon/sound_mute_dark.png")
205ICON_THEME_LIGHT = resource_path("Resources/Icon/theme_light.png")
206ICON_THEME_DARK = resource_path("Resources/Icon/theme_dark.png")
207ICON_LANGUAGE_LIGHT = resource_path("Resources/Icon/language_light.png")
208ICON_LANGUAGE_DARK = resource_path("Resources/Icon/language_dark.png")
209ICON_LANGUAGE_ENGLISH = resource_path("Resources/Icon/language_english.png")
210ICON_LANGUAGE_GERMAN = resource_path("Resources/Icon/language_german.png")
211ICON_LOG_LIGHT = resource_path("Resources/Icon/log_light.png")
212ICON_LOG_DARK = resource_path("Resources/Icon/log_dark.png")
213# Menu Help
214ICON_HELP_LIGHT = resource_path("Resources/Icon/help_light.png")
215ICON_HELP_DARK = resource_path("Resources/Icon/help_dark.png")
216ICON_RESET_LIGHT = resource_path("Resources/Icon/reset_light.png")
217ICON_RESET_DARK = resource_path("Resources/Icon/reset_dark.png")
218HELP_PATH = resource_path("Resources/Help/")
219# Sound
220S_SOUND_CASH_PATH = resource_path("Resources/Sound/cash.wav")
221S_SOUND_CLEAR_PATH = resource_path("Resources/Sound/clear.wav")
222S_SOUND_TOUCH_PATH = resource_path("Resources/Sound/touch.wav")
223S_SOUND_UNLOCK_PATH = resource_path("Resources/Sound/unlock.wav")
224
225
226# Settings Registry
227COMPANY_NAME = "<COMPANY_NAME>"
228APP_NAME = "<APP_NAME>"
229settings_handle = SETTINGS(COMPANY_NAME, APP_NAME)
230
231
232class EPaper(int, enum.Enum):
233 """!
234 @brief Supported paper widths of printer
235 """
236 WIDTH_58_MM = 58
237 WIDTH_80_MM = 80
238
239
240class ETheme(str, enum.Enum):
241 """!
242 @brief Available application themes
243 """
244 AUTO = "auto"
245 LIGHT = "light"
246 DARK = "dark"
247 CLASSIC = "classic"
248 SYSTEM = "system"
249
250
251class ELanguages(str, enum.Enum):
252 """!
253 @brief Available application languages
254 """
255 ENGLISH = "english"
256 GERMAN = "german"
257
258
259S_UNIT = "EUR"
260S_UNIT_SYMBOL = "€"
261S_BREAK_LINE = "\\n"
262
263# report defines
264S_DATE_PRINT_FORMAT = "%Y/%m/%d %H:%M:%S"
265L_PRINT_FILE_HEADER: list[str] = ["Count", "Article Name", "Article Number", "Group", "Total Price", "User", "Date", "Printer"]
266L_GROUP = ["Group", "Gruppe"]
267L_DEVICE = ["Device", "System"]
268
269# Init sections
270S_SECTION_PRINTER = "PRINTER"
271S_KEY_PRINTER_COM_PORT = "com_port"
272S_DEFAULT_COM_PORT = None
273S_KEY_PAPER_WIDTH = "paper_width"
274I_DEFAULT_PAPER_WIDTH = EPaper.WIDTH_58_MM.value
275
276S_SECTION_SETTINGS = "SETTINGS"
277S_KEY_SOUND = "sound"
278B_DEFAULT_SOUND = True
279S_KEY_SHOW_PRICE = "show_price"
280B_DEFAULT_SHOW_PRICE = True
281S_KEY_PRINT_REPORT = "print_report"
282B_DEFAULT_PRINT_REPORT = True
283S_KEY_THEME = "darkmode"
284E_DEFAULT_THEME = ETheme.AUTO
285S_KEY_LANGUAGE = "language"
286E_DEFAULT_LANGUAGE = ELanguages.ENGLISH
287S_KEY_VERBOSITY = "verbosity"
288I_LOG_LEVEL_DEFAULT = logging.WARNING
289S_KEY_OUTPUT_PATH = "output_path"
290S_DEFAULT_OUTPUT_FOLDER = "BonPrinter"
291S_DEFAULT_OUTPUT_PATH = str(Path.home()) + "/" + S_DEFAULT_OUTPUT_FOLDER
292S_KEY_UPDATE_VERSION = "update_version"
293S_DEFAULT_UPDATE_VERSION = "0.0.0"
294S_KEY_LAST_DIR_PATH = "last_dir"
295S_DEFAULT_LAST_PATH = "./"
296
297
298S_KEY_GEOMETRY = "window_geometry"
299S_KEY_STATE = "window_state"
300I_DEFAULT_WIN_WIDTH = 720
301I_DEFAULT_WIN_HEIGHT = 450
302
303S_SECTION_CONFIGURATION = "CONFIGURATION"
304S_KEY_USERS = "USERS"
305S_KEY_ITEMS = "ITEMS"
306
307S_HEADER = "Header"
308S_HEADER_1 = "header1"
309S_HEADER_2 = "header2"
310S_TAX = "Tax"
311S_GROUP_1 = "group1"
312S_GROUP_2 = "group2"
313S_GROUP_3 = "group3"
314S_USER_TAX = "user"
315S_PRINT_ARTICLE = "print_article"
316S_DEFAULT_PRINT_ARTICLE = "True"
317S_PRINT_FREE_PRICE = "print_free_price"
318S_DEFAULT_PRINT_FREE_PRICE = "True"
319S_NAME = "name"
320S_PRICE = "price"
321S_GROUP = "tax_group"
322S_USER_VISIBLE = "user_visible"
323S_PRINT = "print"
324S_MARK = "mark"
325S_BACKGROUND = "bg"
326S_PRINTS = "Prints"
327S_PW = "pw"
328S_DEFAULT_PRICE = "9.00"
329LOCKED_USER_PW = "Locked"
330S_DEFAULT_USER_PW = LOCKED_USER_PW
331RESET_CODE = b"" # code to reset tool
332DEFAULT_CODE = "1234"
333
334S_SECTION_SALES = "SALES"
335I_NUMBER_OF_SALES = 101 # 1 Local + 100 order stuff
336F_DEFAULT_SALES = 0.0 # need to be zero to clear sales
337
338S_GENERAL = "General"
339S_DEVICE = "device"
340S_USER_TIMEOUT = "timeout"
341S_AUTO_OPEN = "auto_open"
342S_FREE_AUTO_LOGOUT = "free_auto_logout"
343I_DEFAULT_USER_TIMEOUT = 15
344S_DEFAULT_AUTO_OPEN = "True"
345S_DEFAULT_FREE_AUTO_LOGOUT = "True"
346
347
348class ItemNumber(enum.IntEnum):
349 """!
350 @brief Item number
351 """
352 MIN = 30 # 5 x 6
353 EXT = 42 # 6 x 7
354 MAX = 56 # 7 x 8
355
356
357D_ITEM_NUMBER_ROW = {
358 ItemNumber.MIN: 6,
359 ItemNumber.EXT: 7,
360 ItemNumber.MAX: 8
361}
362
363ITEM_NUMBER_COLUMN_MIN = 5
364
365D_ITEM_NUMBER_COLUMN = {
366 ItemNumber.MIN: ITEM_NUMBER_COLUMN_MIN,
367 ItemNumber.EXT: 6,
368 ItemNumber.MAX: 7
369}
370
371I_ITEM_ARRAY_SIZE = ItemNumber.MAX
372I_NUMBER_OF_DISPLAYED_USER = 15
373
374I_GROUP_1 = 1
375I_GROUP_2 = 2
376I_GROUP_3 = 3
377
378
379class EUser(str, enum.Enum):
380 """!
381 @brief Present users.
382 """
383 B = "B"
384 USER = "Smart\nCard"
385 LOCAL = "Local"
386 FREE = "Free"
387 HOST = "Host"
388 ADMIN = "Admin"
389
390
391L_CONFIG_USER = [EUser.ADMIN, EUser.HOST]
392L_SELL_USER = [EUser.LOCAL, EUser.FREE]
393L_ORDER_USER = [EUser.B + str(i) for i in range(1, I_NUMBER_OF_SALES)]
394L_ALLOWED_USER = L_CONFIG_USER + L_SELL_USER + L_ORDER_USER
395
396D_DEFAULT_USER = {
397 S_GENERAL: {S_DEVICE: "",
398 S_USER_TIMEOUT: str(I_DEFAULT_USER_TIMEOUT),
399 S_AUTO_OPEN: str(S_DEFAULT_AUTO_OPEN),
400 S_FREE_AUTO_LOGOUT: str(S_DEFAULT_FREE_AUTO_LOGOUT)},
401 EUser.ADMIN.value: {S_PW: base64.b64decode(RESET_CODE).decode("utf-8")},
402 EUser.HOST.value: {S_PW: LOCKED_USER_PW},
403 EUser.LOCAL.value: {S_PW: LOCKED_USER_PW},
404 EUser.FREE.value: {S_PW: LOCKED_USER_PW}
405}
406
407for i in range(0, I_NUMBER_OF_DISPLAYED_USER):
408 user_key = f"{EUser.B.value}{i + 1}"
409 D_DEFAULT_USER[user_key] = {}
410 D_DEFAULT_USER[user_key][S_NAME] = ""
411 D_DEFAULT_USER[user_key][S_PW] = S_DEFAULT_USER_PW
412
413D_DEFAULT_ITEM = {
414 S_HEADER: {S_HEADER_1: "", S_HEADER_2: ""},
415 S_TAX: {S_GROUP_1: "19", S_GROUP_2: "19", S_GROUP_3: "0", S_USER_TAX: "0"},
416 S_PRINTS: {
417 S_PRINT_ARTICLE: S_DEFAULT_PRINT_ARTICLE,
418 S_PRINT_FREE_PRICE: S_DEFAULT_PRINT_FREE_PRICE
419 },
420}
421
422for i in range(0, ItemNumber.MAX):
423 item_key = str(i + 1)
424 D_DEFAULT_ITEM[item_key] = {}
425 D_DEFAULT_ITEM[item_key][S_NAME] = ""
426 D_DEFAULT_ITEM[item_key][S_PRICE] = S_DEFAULT_PRICE
427 D_DEFAULT_ITEM[item_key][S_GROUP] = str(I_GROUP_3)
428 # D_DEFAULT_ITEM[item_key][S_USER_VISIBLE] = "" # not visible in default view
429 # D_DEFAULT_ITEM[item_key][S_PRINT] = "" # not visible in default view
430 # D_DEFAULT_ITEM[item_key][S_MARK] = "" # not visible in default view
431 # D_DEFAULT_ITEM[item_key][S_BACKGROUND] = "" # not visible in default view
432
433
434def get_default_item_config(s_deposit_name: Optional[str] = None) -> dict[str, dict[str, str]]:
435 """!
436 @brief Get default article configuration depend on deposit name
437 @param s_deposit_name : name of deposit
438 @return default article configuration
439 """
440 d_item_dict = copy.deepcopy(D_DEFAULT_ITEM)
441 return d_item_dict
442
443
444def clear_settings() -> None:
445 """!
446 @brief Clear registry settings to write defaults at next startup
447 """
448 log.warning("Set default configuration settings")
449 handle = get_settings_handle()
450 # do not delete group S_SECTION_SALES
451 for group in [S_SECTION_PRINTER, S_SECTION_CONFIGURATION, S_SECTION_SETTINGS]:
452 handle.beginGroup(group)
453 handle.remove("") # delete group
454 handle.endGroup()
455
456
457def get_settings_handle() -> SETTINGS:
458 """!
459 @brief Returns the settings handle
460 @return settings handle
461 """
462 return settings_handle
463
464
465def get_registry_value(handle: SETTINGS, s_key: str, b_none_err: bool = True) -> Any:
466 """!
467 @brief Reads the registry value with given handle and key.
468 @param handle : settings handle
469 @param s_key : get value for this key
470 @param b_none_err : [True] call error if None in setting [False] allowed None as setting
471 @return value that is mapped to the given key or raises a KeyError if key not found in handle.
472 """
473 value = handle.value(s_key, defaultValue=None)
474 if b_none_err and (value is None):
475 raise KeyError(f"{s_key} not found in group {handle.group()}")
476 return value
477
478
479def write_sound_settings(b_sound_status: bool) -> None:
480 """!
481 @brief Writes the sound settings to persistent storage
482 @param b_sound_status : sound status
483 """
484 handle = get_settings_handle()
485 handle.beginGroup(S_SECTION_SETTINGS)
486 handle.setValue(S_KEY_SOUND, b_sound_status)
487 handle.endGroup()
488
489
491 """!
492 @brief Reads the sound settings from persistent storage
493 @return sound status
494 """
495 handle = get_settings_handle()
496 try:
497 handle.beginGroup(S_SECTION_SETTINGS)
498 b_sound = bool(get_registry_value(handle, S_KEY_SOUND) == "true")
499 handle.endGroup()
500 except BaseException as e:
501 log.debug("Sound not found, using default values (%s)", str(e))
502 b_sound = B_DEFAULT_SOUND
503 handle.endGroup()
504 return b_sound
505
506
507def write_sales(i_user_pos: int, f_sales: float) -> None:
508 """!
509 @brief Writes the sales to persistent storage
510 @param i_user_pos : user position to write sales
511 @param f_sales : sales value to save
512 """
513 handle = get_settings_handle()
514 handle.beginGroup(S_SECTION_SALES)
515 handle.setValue(str(i_user_pos), f_sales)
516 handle.endGroup()
517
518
519def read_sales() -> list[float]:
520 """!
521 @brief Reads the sales from persistent storage
522 @return sales
523 """
524 handle = get_settings_handle()
525 handle.beginGroup(S_SECTION_SALES)
526 l_sales = []
527 for i_user in range(I_NUMBER_OF_SALES):
528 try:
529 l_sales.append(float(get_registry_value(handle, str(i_user))))
530 except KeyError as e:
531 log.debug("Sales not found, using default values (%s)", str(e))
532 l_sales.append(F_DEFAULT_SALES)
533 except BaseException:
534 # set sales as warning to save value
535 log.warning("Sales of user %s not valid: %s", str(i_user), get_registry_value(handle, str(i_user)))
536 l_sales.append(F_DEFAULT_SALES)
537 handle.endGroup()
538 return l_sales
539
540
541def write_user_settings(d_user: dict[str, dict[str, str]]) -> None:
542 """!
543 @brief Writes the user settings to persistent storage. Passwords are stored encrypted.
544 This encryption is carried out via the Windows Data Protection API (DPAPI),
545 which enables user and machine-specific encryption.
546 @param d_user : user dictionary
547 """
548 dict_to_write = copy.deepcopy(d_user)
549 if platform.system() == 'Windows':
550 for user_type, user_data in dict_to_write.items(): # protect password
551 if S_PW in user_data:
552 s_password = user_data[S_PW]
553 s_password_encrypted = win32crypt.CryptProtectData(bytes(s_password, "utf-8")) if len(s_password) > 0 else ""
554 dict_to_write[user_type][S_PW] = s_password_encrypted
555 handle = get_settings_handle()
556 handle.beginGroup(S_SECTION_CONFIGURATION)
557 handle.setValue(S_KEY_USERS, dict_to_write)
558 handle.endGroup()
559
560
561def read_user_settings() -> dict[str, Any]:
562 """!
563 @brief Reads the user settings from persistent storage
564 @return user settings
565 """
566 handle = get_settings_handle()
567 try:
568 handle.beginGroup(S_SECTION_CONFIGURATION)
569 d_user = get_registry_value(handle, S_KEY_USERS)
570 if platform.system() == 'Windows':
571 for user_type, user_data in d_user.items():
572 if S_PW in user_data:
573 bytes_password_enc = user_data[S_PW]
574 _, bytes_password = win32crypt.CryptUnprotectData(bytes_password_enc) if (len(bytes_password_enc) > 0) else (None, bytes(bytes_password_enc, "utf-8"))
575 d_user[user_type][S_PW] = bytes_password.decode("utf-8")
576 handle.endGroup()
577 except BaseException as e:
578 log.debug("Invalid User settings, using default values (%s)", str(e))
579 d_user = D_DEFAULT_USER
580 handle.endGroup()
581 return d_user if isinstance(d_user, dict) else {}
582
583
584def write_articles_settings(d_articles: dict[str, dict[str, str]]) -> None:
585 """!
586 @brief Writes the articles settings to persistent storage
587 @param d_articles : articles dictionary
588 """
589 handle = get_settings_handle()
590 handle.beginGroup(S_SECTION_CONFIGURATION)
591 handle.setValue(S_KEY_ITEMS, d_articles)
592 handle.endGroup()
593
594
595def read_articles_settings() -> dict[str, Any]:
596 """!
597 @brief Reads the articles settings from persistent storage
598 @return user settings
599 """
600 handle = get_settings_handle()
601 try:
602 handle.beginGroup(S_SECTION_CONFIGURATION)
603 d_articles = get_registry_value(handle, S_KEY_ITEMS)
604 handle.endGroup()
605 except BaseException as e:
606 log.debug("Articles settings not found, using default values (%s)", str(e))
607 d_articles = D_DEFAULT_ITEM
608 handle.endGroup()
609 return d_articles if isinstance(d_articles, dict) else {}
610
611
612def write_com_port_settings(s_com_port: str) -> None:
613 """!
614 @brief Writes the COM port settings to persistent storage
615 @param s_com_port : COM port
616 """
617 handle = get_settings_handle()
618 handle.beginGroup(S_SECTION_PRINTER)
619 handle.setValue(S_KEY_PRINTER_COM_PORT, s_com_port)
620 handle.endGroup()
621
622
623def read_com_port_settings() -> str | None:
624 """!
625 @brief Reads the COM port settings from persistent storage
626 @return COM port
627 """
628 handle = get_settings_handle()
629 com_port: str | None
630 try:
631 handle.beginGroup(S_SECTION_PRINTER)
632 com_port = get_registry_value(handle, S_KEY_PRINTER_COM_PORT, b_none_err=False)
633 if com_port is not None:
634 com_port = str(com_port)
635 handle.endGroup()
636 except BaseException as e:
637 log.debug("COM Port settings not found, using default values (%s)", str(e))
638 com_port = S_DEFAULT_COM_PORT
639 handle.endGroup()
640 return com_port
641
642
643def write_paper_width_settings(i_paper_width: int) -> None:
644 """!
645 @brief Writes the paper width settings to persistent storage
646 @param i_paper_width : paper width
647 """
648 handle = get_settings_handle()
649 handle.beginGroup(S_SECTION_PRINTER)
650 handle.setValue(S_KEY_PAPER_WIDTH, i_paper_width)
651 handle.endGroup()
652
653
655 """!
656 @brief Reads the paper width settings from persistent storage
657 @return paper width
658 """
659 handle = get_settings_handle()
660 try:
661 handle.beginGroup(S_SECTION_PRINTER)
662 i_paper_width = int(get_registry_value(handle, S_KEY_PAPER_WIDTH))
663 handle.endGroup()
664 except BaseException as e:
665 log.debug("Paper Width settings not found, using default values (%s)", str(e))
666 i_paper_width = I_DEFAULT_PAPER_WIDTH
667 handle.endGroup()
668 return i_paper_width
669
670
671def write_output_path_settings(s_output_path: str) -> None:
672 """!
673 @brief Writes the output path settings to persistent storage
674 @param s_output_path : output path
675 """
676 handle = get_settings_handle()
677 handle.beginGroup(S_SECTION_PRINTER)
678 handle.setValue(S_KEY_OUTPUT_PATH, s_output_path)
679 handle.endGroup()
680
681
683 """!
684 @brief Reads the output path settings from persistent storage
685 @return output path
686 """
687 handle = get_settings_handle()
688 try:
689 handle.beginGroup(S_SECTION_PRINTER)
690 s_output_path = str(get_registry_value(handle, S_KEY_OUTPUT_PATH))
691 handle.endGroup()
692 except BaseException as e:
693 log.debug("Output path settings not found, using default values (%s)", str(e))
694 if not os.path.exists(S_DEFAULT_OUTPUT_PATH):
695 os.mkdir(S_DEFAULT_OUTPUT_PATH)
696 s_output_path = S_DEFAULT_OUTPUT_PATH
697 handle.endGroup()
698 return s_output_path
699
700
701def write_theme_settings(e_theme: ETheme) -> None:
702 """!
703 @brief Writes the theme settings to persistent storage
704 @param e_theme : current theme
705 """
706 handle = get_settings_handle()
707 handle.beginGroup(S_SECTION_SETTINGS)
708 handle.setValue(S_KEY_THEME, e_theme.value)
709 handle.endGroup()
710
711
712def read_theme_settings() -> ETheme:
713 """!
714 @brief Reads the theme settings from persistent storage
715 @return Theme settings (enum ETheme)
716 """
717 handle = get_settings_handle()
718 try:
719 handle.beginGroup(S_SECTION_SETTINGS)
720 s_theme = get_registry_value(handle, S_KEY_THEME)
721 e_theme = ETheme(s_theme)
722 handle.endGroup()
723 except BaseException as e:
724 log.debug("Theme settings not found, using default values (%s)", str(e))
725 e_theme = E_DEFAULT_THEME
726 handle.endGroup()
727 return e_theme
728
729
730def write_language(e_language: ELanguages) -> None:
731 """!
732 @brief Writes the language to persistent storage
733 @param e_language : current language
734 """
735 handle = get_settings_handle()
736 handle.beginGroup(S_SECTION_SETTINGS)
737 handle.setValue(S_KEY_LANGUAGE, e_language.value)
738 handle.endGroup()
739
740
741def read_language() -> ELanguages:
742 """!
743 @brief Reads the language from persistent storage
744 @return Language (enum ELanguages)
745 """
746 handle = get_settings_handle()
747 try:
748 handle.beginGroup(S_SECTION_SETTINGS)
749 s_language = get_registry_value(handle, S_KEY_LANGUAGE)
750 e_language = ELanguages(s_language)
751 handle.endGroup()
752 except BaseException as e:
753 log.debug("Language not found, using default values (%s)", str(e))
754 current_locale, _test = locale.getlocale() # check keyboard layout
755 if current_locale is not None: # TODO is None if start with EXE
756 e_language = ELanguages.GERMAN if current_locale.startswith("de") else E_DEFAULT_LANGUAGE
757 else:
758 e_language = E_DEFAULT_LANGUAGE
759 handle.endGroup()
760 return e_language
761
762
763def write_verbosity_settings(i_log_level: int) -> None:
764 """!
765 @brief Writes the verbosity settings to persistent storage
766 @param i_log_level : log verbosity
767 """
768 handle = get_settings_handle()
769 handle.beginGroup(S_SECTION_SETTINGS)
770 handle.setValue(S_KEY_VERBOSITY, i_log_level)
771 handle.endGroup()
772
773
775 """!
776 @brief Reads the verbosity settings from persistent storage
777 @return verbosity level
778 """
779 handle = get_settings_handle()
780 try:
781 handle.beginGroup(S_SECTION_SETTINGS)
782 i_log_level = int(get_registry_value(handle, S_KEY_VERBOSITY))
783 handle.endGroup()
784 except BaseException as e:
785 log.debug("Verbosity settings not found, using default values (%s)", str(e))
786 i_log_level = I_LOG_LEVEL_DEFAULT
787 handle.endGroup()
788 return i_log_level
789
790
791def save_window_state(o_geometry: BYTE_ARRAY, o_state: BYTE_ARRAY) -> None:
792 """!
793 @brief Saves the window state to persistent storage.
794 @param o_geometry : geometry (position, size) of the window as QByteArray
795 @param o_state : state (dock widgets etc.) of the window as QByteArray
796 """
797 handle = get_settings_handle()
798 handle.beginGroup(S_SECTION_SETTINGS)
799 handle.setValue(S_KEY_GEOMETRY, o_geometry)
800 handle.setValue(S_KEY_STATE, o_state)
801 handle.endGroup()
802
803
804def read_window_state() -> tuple[BYTE_ARRAY, BYTE_ARRAY]:
805 """!
806 @brief Reads the window geometry and state from persistent storage.
807 @return window geometry and state as QByteArray
808 """
809 handle = get_settings_handle()
810 try:
811 handle.beginGroup(S_SECTION_SETTINGS)
812 o_geometry = get_registry_value(handle, S_KEY_GEOMETRY)
813 o_state = get_registry_value(handle, S_KEY_STATE)
814 handle.endGroup()
815 except BaseException as e:
816 log.debug("WindowsSettings not found, using default values (%s)", str(e))
817 o_geometry = o_state = None
818 handle.endGroup()
819 return o_geometry, o_state
820
821
822def write_update_version(version: str) -> None:
823 """!
824 @brief Writes the last reminded tool version for update to persistent storage.
825 @param version : last reminded version
826 """
827 handle = get_settings_handle()
828 handle.beginGroup(S_SECTION_SETTINGS)
829 handle.setValue(S_KEY_UPDATE_VERSION, version)
830 handle.endGroup()
831
832
834 """!
835 @brief Reads the last reminded tool version from persistent storage
836 @return last reminded version
837 """
838 handle = get_settings_handle()
839 try:
840 handle.beginGroup(S_SECTION_SETTINGS)
841 version = str(get_registry_value(handle, S_KEY_UPDATE_VERSION))
842 handle.endGroup()
843 except BaseException as e:
844 log.debug("Update version settings not found, using default values (%s)", str(e))
845 version = S_DEFAULT_UPDATE_VERSION
846 handle.endGroup()
847 return version
848
849
850def write_last_dir(dir_path: str) -> None:
851 """!
852 @brief Writes the last directory to persistent storage
853 @param dir_path : directory path
854 """
855 handle = get_settings_handle()
856 handle.beginGroup(S_SECTION_SETTINGS)
857 handle.setValue(S_KEY_LAST_DIR_PATH, dir_path)
858 handle.endGroup()
859
860
861def read_last_dir() -> str:
862 """!
863 @brief Reads the last directory from persistent storage
864 @return directory path
865 """
866 handle = get_settings_handle()
867 try:
868 handle.beginGroup(S_SECTION_SETTINGS)
869 dir_path = str(get_registry_value(handle, S_KEY_LAST_DIR_PATH))
870 handle.endGroup()
871 except BaseException as e:
872 log.debug("Last directory settings not found, using default values (%s)", str(e))
873 dir_path = S_DEFAULT_LAST_PATH
874 handle.endGroup()
875 return dir_path
876
877
878def write_print_report_settings(b_print_report: bool) -> None:
879 """!
880 @brief Writes the print report settings to persistent storage
881 @param b_print_report : enable print report status
882 """
883 handle = get_settings_handle()
884 handle.beginGroup(S_SECTION_SETTINGS)
885 handle.setValue(S_KEY_PRINT_REPORT, b_print_report)
886 handle.endGroup()
887
888
890 """!
891 @brief Reads the print report status from persistent storage
892 @return print report status
893 """
894 handle = get_settings_handle()
895 try:
896 handle.beginGroup(S_SECTION_SETTINGS)
897 b_show_price = bool(get_registry_value(handle, S_KEY_PRINT_REPORT) == "true")
898 handle.endGroup()
899 except BaseException as e:
900 log.debug("Print Report not found, using default values (%s)", str(e))
901 b_show_price = B_DEFAULT_PRINT_REPORT
902 handle.endGroup()
903 return b_show_price
904
905
906def write_show_price_settings(b_show_price: bool) -> None:
907 """!
908 @brief Writes the show price settings to persistent storage
909 @param b_show_price : show price status
910 """
911 handle = get_settings_handle()
912 handle.beginGroup(S_SECTION_SETTINGS)
913 handle.setValue(S_KEY_SHOW_PRICE, b_show_price)
914 handle.endGroup()
915
916
918 """!
919 @brief Reads the show price status from persistent storage
920 @return show price status
921 """
922 handle = get_settings_handle()
923 try:
924 handle.beginGroup(S_SECTION_SETTINGS)
925 b_show_price = bool(get_registry_value(handle, S_KEY_SHOW_PRICE) == "true")
926 handle.endGroup()
927 except BaseException as e:
928 log.debug("Show Price not found, using default values (%s)", str(e))
929 b_show_price = B_DEFAULT_SHOW_PRICE
930 handle.endGroup()
931 return b_show_price
Available application languages.
Definition app_data.py:251
Supported paper widths of printer.
Definition app_data.py:232
Available application themes.
Definition app_data.py:240
bool read_show_price_settings()
Reads the show price status from persistent storage.
Definition app_data.py:917
str read_output_path_settings()
Reads the output path settings from persistent storage.
Definition app_data.py:682
dict[str, dict[str, str]] get_default_item_config(Optional[str] s_deposit_name=None)
Get default article configuration depend on deposit name.
Definition app_data.py:434
ELanguages read_language()
Reads the language from persistent storage.
Definition app_data.py:741
None write_last_dir(str dir_path)
Writes the last directory to persistent storage.
Definition app_data.py:850
None write_show_price_settings(bool b_show_price)
Writes the show price settings to persistent storage.
Definition app_data.py:906
int read_verbosity_settings()
Reads the verbosity settings from persistent storage.
Definition app_data.py:774
None save_window_state(BYTE_ARRAY o_geometry, BYTE_ARRAY o_state)
Saves the window state to persistent storage.
Definition app_data.py:791
tuple[bool, bool|None] reset_config_dialog("MainWindow | None" ui)
Show reset configuration dialog.
Definition app_data.py:76
str read_update_version()
Reads the last reminded tool version from persistent storage.
Definition app_data.py:833
None write_com_port_settings(str s_com_port)
Writes the COM port settings to persistent storage.
Definition app_data.py:612
None clear_settings()
Clear registry settings to write defaults at next startup.
Definition app_data.py:444
None write_output_path_settings(str s_output_path)
Writes the output path settings to persistent storage.
Definition app_data.py:671
ETheme read_theme_settings()
Reads the theme settings from persistent storage.
Definition app_data.py:712
None write_paper_width_settings(int i_paper_width)
Writes the paper width settings to persistent storage.
Definition app_data.py:643
bool read_sound_settings()
Reads the sound settings from persistent storage.
Definition app_data.py:490
str get_computer_name()
Get computer name.
Definition app_data.py:97
None open_explorer(str explorer_path, bool b_open_input=False)
Open explorer.
Definition app_data.py:109
SETTINGS get_settings_handle()
Returns the settings handle.
Definition app_data.py:457
None write_theme_settings(ETheme e_theme)
Writes the theme settings to persistent storage.
Definition app_data.py:701
dict[str, Any] read_articles_settings()
Reads the articles settings from persistent storage.
Definition app_data.py:595
None write_print_report_settings(bool b_print_report)
Writes the print report settings to persistent storage.
Definition app_data.py:878
list[float] read_sales()
Reads the sales from persistent storage.
Definition app_data.py:519
None write_language(ELanguages e_language)
Writes the language to persistent storage.
Definition app_data.py:730
None write_user_settings(dict[str, dict[str, str]] d_user)
Writes the user settings to persistent storage.
Definition app_data.py:541
str|None read_com_port_settings()
Reads the COM port settings from persistent storage.
Definition app_data.py:623
None write_articles_settings(dict[str, dict[str, str]] d_articles)
Writes the articles settings to persistent storage.
Definition app_data.py:584
list[ListPortInfo] get_serial_ports()
Check if serial device is present at this COM port.
Definition app_data.py:138
Any get_registry_value(SETTINGS handle, str s_key, bool b_none_err=True)
Reads the registry value with given handle and key.
Definition app_data.py:465
int read_paper_width_settings()
Reads the paper width settings from persistent storage.
Definition app_data.py:654
bool read_print_report_settings()
Reads the print report status from persistent storage.
Definition app_data.py:889
None write_sales(int i_user_pos, float f_sales)
Writes the sales to persistent storage.
Definition app_data.py:507
dict[str, Any] read_user_settings()
Reads the user settings from persistent storage.
Definition app_data.py:561
None thread_loop(Any obj, str thread_name)
loop for threads.
Definition app_data.py:52
tuple[BYTE_ARRAY, BYTE_ARRAY] read_window_state()
Reads the window geometry and state from persistent storage.
Definition app_data.py:804
None write_update_version(str version)
Writes the last reminded tool version for update to persistent storage.
Definition app_data.py:822
str read_last_dir()
Reads the last directory from persistent storage.
Definition app_data.py:861
None write_verbosity_settings(int i_log_level)
Writes the verbosity settings to persistent storage.
Definition app_data.py:763
bool check_serial_device(str s_com_port)
Check if serial device is present at this COM port.
Definition app_data.py:121
None write_sound_settings(bool b_sound_status)
Writes the sound settings to persistent storage.
Definition app_data.py:479
str resource_path(str s_relative_path)
Returns the absolute path to a resource given by a relative path depending on the environment (EXE or...
Definition app_data.py:36