2********************************************************************************
4@brief View controller for the main window
5********************************************************************************
13from typing
import Optional, Callable, Any
14from datetime
import datetime
21from Source.Util.app_data import EUser, L_CONFIG_USER, ETheme, EPaper, S_PW, I_GROUP_3, ELanguages, \
22 read_sound_settings, save_window_state, D_DEFAULT_USER, RESET_CODE, ICON_APP, \
23 ICON_HELP_LIGHT, S_UNIT_SYMBOL, ICON_REPORT_LIGHT, ICON_PRINTER_LIGHT, open_explorer, \
24 DEFAULT_CODE, ICON_USER_RESET_LIGHT, ICON_ARTICLES_RESET_LIGHT, reset_config_dialog, ICON_HELP_DARK, \
25 write_update_version, read_update_version, I_ITEM_ARRAY_SIZE, ItemNumber, get_default_item_config
29 emit_signal, config_window, get_window_geometry, get_window_state, \
30 filter_menubar, connect_menu, get_menu_text, config_menu, group_menu, \
31 connect_button, config_btn, get_btn_text, config_text, LABEL, config_label, \
32 config_statusbar, config_table, insert_table_item, get_selected_table_row, get_selected_table_column, \
33 open_message_box, input_dialog, open_directory, open_file, save_file, copy_to_clipboard, create_timer, \
34 ACTION, RESIZE_EVENT, CLOSE_EVENT, close_app, BUTTON
42from Source.Model.config import write_config_to_file, S_USER_TEMP_FILE, S_USER_FILE, S_ITEM_FILE, S_ITEM_TEMP_FILE
43from Source.Model.language import L_SAVE, L_PRINT, L_OPEN, L_CLEAR, L_MENU_HELP, L_CLOSE, L_YES, L_NO, L_CANCEL, \
44 L_COPY_URL, L_YOU_SURE, L_MENU_USER
49log = logging.getLogger(__title__)
51B_NOTEPAD_REPORT =
False
54 S_REPORT_TEMP_FILE =
"_temp_Report.md"
60L_SPECIAL_BTNS = [S_NULL, S_DOUBLE_NULL, S_DOT]
61I_BTN_LONG_PRESS_TIME = 1000
64L_LOGIN_KEYS = [S_NULL,
"1",
"4",
"7", EUser.B.value +
"1",
65 EUser.ADMIN.value,
"2",
"5",
"8", EUser.B.value +
"2",
66 EUser.HOST.value,
"3",
"6",
"9", EUser.B.value +
"3",
67 EUser.FREE.value, EUser.B.value +
"13", EUser.B.value +
"10", EUser.B.value +
"7", EUser.B.value +
"4",
68 EUser.LOCAL.value, EUser.B.value +
"14", EUser.B.value +
"11", EUser.B.value +
"8", EUser.B.value +
"5",
69 EUser.USER.value, EUser.B.value +
"15", EUser.B.value +
"12", EUser.B.value +
"9", EUser.B.value +
"6"]
72STATUS_TEXT_TIME = 3000
73STATUS_HIGHLIGHT_TEXT_TIME = 5000
74STATUS_WARNING_TEXT_TIME = 15000
77WARNING_STYLE =
"orange"
78WARNING_STYLE_DARK =
"darkorange"
79HIGHLIGHT_STYLE =
"red"
85 @brief Mode of configuration change.
91 PDF_IMPORT =
"pdf_import"
94INI_FILE_TYPES = (
"INI file",
"*.ini")
95LOG_FILE_TYPES = (
"CSV file",
"*.csv")
96REPORT_FILE_TYPES = (
"MD file",
"*.md")
97I_MAX_ITEMS_TO_PRINT = 50
102 @brief Connect menu for COM ports.
103 @param menu : connect function to this menu
104 @param function : function to connect
106 menu_text = get_menu_text(menu)
107 connect_menu(menu, function, menu_text.split(maxsplit=1)[0])
112 @brief The view-controller for main window. Entry point of application.
113 Provides general methods that may be called by any other controller.
114 @param qt_exception_hook : exception hook
115 @param log_config : log configuration of the application
116 @param test_mode : status if application run in test mode for pytest
117 @param authenticated : status if you are authenticated to use this program
121 def __init__(self, qt_exception_hook: UncaughtHook, log_config: LogConfig, test_mode: bool =
False,
122 authenticated: bool =
False, *args: Any, **kwargs: Any) ->
None:
123 log.debug(
"Initializing Main Window")
131 config_window(self, title=__title__)
140 log.debug(
"Initializing main configuration")
158 self.
model.c_monitor.update_darkmode_status(self.
model.c_monitor.e_style)
160 self.
model.c_sound.update_sound_status(read_sound_settings())
166 self.
model.log_config.i_log_level,
167 [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG])
172 self.
model.c_printer.i_paper_width,
173 [EPaper.WIDTH_58_MM, EPaper.WIDTH_80_MM])
187 self.
model.c_monitor.e_style,
188 [ETheme.AUTO, ETheme.LIGHT, ETheme.DARK, ETheme.SYSTEM])
193 self.
model.c_language.e_language,
194 [ELanguages.ENGLISH, ELanguages.GERMAN])
198 self.
l_bnt_fnc: list[BUTTON] = [self.btn_item1, self.btn_item2, self.btn_item3, self.btn_item4, self.btn_item5,
199 self.btn_item6, self.btn_item7, self.btn_item8, self.btn_item9, self.btn_item10,
200 self.btn_item11, self.btn_item12, self.btn_item13, self.btn_item14, self.btn_item15,
201 self.btn_item16, self.btn_item17, self.btn_item18, self.btn_item19, self.btn_item20,
202 self.btn_item21, self.btn_item22, self.btn_item23, self.btn_item24, self.btn_item25,
203 self.btn_item26, self.btn_item27, self.btn_item28, self.btn_item29, self.btn_item30,
204 self.btn_item31, self.btn_item32, self.btn_item33, self.btn_item34, self.btn_item35,
205 self.btn_item36, self.btn_item37, self.btn_item38, self.btn_item39, self.btn_item40,
206 self.btn_item41, self.btn_item42, self.btn_item43, self.btn_item44, self.btn_item45,
207 self.btn_item46, self.btn_item47, self.btn_item48, self.btn_item49, self.btn_item50,
208 self.btn_item51, self.btn_item52, self.btn_item53, self.btn_item54, self.btn_item55,
259 connect_menu(self.
action_58mm, self.
model.c_printer.update_paper_width, EPaper.WIDTH_58_MM.value)
260 connect_menu(self.
action_80mm, self.
model.c_printer.update_paper_width, EPaper.WIDTH_80_MM.value)
288 self.
model.c_report.create_report(b_clear_report=
False, b_startup_check=
True)
289 config_window(self, fullscreen=
False, resize_callback=self.
model.c_monitor.resize_window)
290 self.
model.c_monitor.resize_window()
292 if not authenticated:
295 self.
model.c_config.read_user_file(
"user_example.ini")
296 self.
model.c_config.read_item_file(
"articles_example.ini")
298 newer_tool_version = get_tool_update_status()
299 if (newer_tool_version
is not None)
and (compare_versions(read_update_version(), newer_tool_version)):
301 if self.
model.c_config.d_user == D_DEFAULT_USER:
307 @brief Default resize Event Method to handle change of window size
308 @param _event : arrived event
314 @brief Default close Event Method to handle application close
315 @param event : arrived event
319 log.debug(
"Close Event")
320 save_window_state(get_window_geometry(self), get_window_state(self))
322 log.debug(
"Application closed")
323 self.
model.c_printer.terminate()
328 def confirm_dialog(self, title_value: str | list[str], s_icon_path: str = ICON_APP, l_optional_text: Optional[list[str]] =
None) -> bool:
330 @brief Show confirm dialog to accept or cancel.
331 @param title_value : dialog title
332 @param s_icon_path : icon of dialog
333 @param l_optional_text : optional text
334 @return return accept status
336 title = self.
model.c_language.get_language_text(title_value)
if isinstance(title_value, list)
else title_value
337 fix_text = self.
model.c_language.get_language_text(L_YOU_SURE)
338 if l_optional_text
is None:
341 text = fix_text +
"\n\n" + self.
model.c_language.get_language_text(l_optional_text)
342 b_accept, _ = open_message_box(parent=self,
345 btn_yes=self.
model.c_language.get_language_text(L_YES),
346 btn_cancel=self.
model.c_language.get_language_text(L_CANCEL),
347 window_icon=s_icon_path)
352 @brief Blocks/Unblock the main UI elements.
353 @param b_state : state if UI should blocked or unblocked, True: Enable, False: Disable
357 config_btn(btn, enable=b_state)
359 config_btn(self.
btn_print, enable=b_state)
360 config_btn(self.
btn_clear, enable=b_state)
361 config_btn(self.
btn_multi, enable=b_state)
362 config_btn(self.
btn_calc, enable=b_state)
363 config_btn(self.
btn_lock, enable=b_state)
371 @brief Blocks the main UI elements.
373 log.debug(
"Block UI")
380 @brief Unblock the main UI elements.
382 log.debug(
"Unblock UI")
391 @brief Show Update dialog.
392 @param newer_tool_version : newest tool version
394 l_text = [f
"New version available! \nUpdate to version {newer_tool_version}?",
395 f
"Neue Version verfügbar! \nUpdate auf Version {newer_tool_version} durchführen?"]
396 l_remember = [
"Don't show again",
397 "Nicht mehr anzeigen"]
398 s_title = self.
model.c_language.get_language_text([
"Update Service",
"Update-Dienst"])
399 b_update, is_checked = open_message_box(parent=self,
401 text=self.
model.c_language.get_language_text(l_text),
402 btn_yes=self.
model.c_language.get_language_text(L_YES),
403 btn_no=self.
model.c_language.get_language_text(L_NO),
404 check_box=self.
model.c_language.get_language_text(l_remember),
405 window_icon=ICON_APP)
407 write_update_version(newer_tool_version)
409 copy_to_clipboard(S_UPDATE_URL)
410 webbrowser.open_new_tab(S_UPDATE_URL)
415 @brief Show welcome screen and choose admin password.
417 l_text = [
"To change user and articles configuration, see the Help menu."
418 +
"\n\nIf the Admin password is lost, all data can be reset:"
419 +
"\n1. Select Admin User to show hidden menu"
420 +
"\n2. Help→Reset"
421 + f
"\n3. Enter reset code: {base64.b64decode(RESET_CODE).decode('utf-8')}"
422 +
"\n\nChoose an Admin password (numbers only):",
423 "Um die Benutzer- und Artikelkonfiguration zu ändern, siehe im Menü Hilfe"
424 +
"\n\nWenn das Admin-Passwort verloren geht, können alle Daten zurückgesetzt werden:"
425 +
"\n1. Wählen Sie Admin User, um das ausgeblendete Menü anzuzeigen"
426 +
"\n2. Help→Reset"
427 + f
"\n3. Reset-Code eingeben: {base64.b64decode(RESET_CODE).decode('utf-8')}"
428 +
"\n\nWählen Sie ein Admin-Passwort (nur Zahlen):"]
429 s_title = self.
model.c_language.get_language_text([
"Welcome to",
"Willkommen zu"])
430 password, ok = input_dialog(self, f
"{s_title} {__title__}", self.
model.c_language.get_language_text(l_text), DEFAULT_CODE, icon=ICON_APP)
431 if ok
and (password.isdigit()
or password ==
""):
432 self.
model.c_config.d_user[EUser.ADMIN.value][S_PW] = str(password)
433 self.
model.c_config.store_user_data()
439 @brief Show unauthenticated dialog.
441 s_text = self.
model.c_language.get_language_text([
"Unauthenticated use",
"Unauthentifizierte Verwendung"])
442 l_text = [
"You are not entitled to test a pre-release version."
443 +
"\n\nUse a published version:"
445 "Sie sind nicht berechtigt, eine Vorabversion zu testen."
446 +
"\n\nVerwenden Sie eine veröffentlichte Version:"
448 b_accept, _ = open_message_box(parent=self,
450 text=self.
model.c_language.get_language_text(l_text),
451 btn_close=self.
model.c_language.get_language_text(L_CLOSE),
452 btn_special=self.
model.c_language.get_language_text(L_COPY_URL),
453 window_icon=ICON_APP)
455 copy_to_clipboard(__home__)
460 @brief Show help dialog.
462 log.debug(
"Starting help dialog")
463 icon = ICON_HELP_LIGHT
if self.
model.c_monitor.is_light_theme()
else ICON_HELP_DARK
464 config_window(self.
dialog_help, title=self.
model.c_language.get_language_text(L_MENU_HELP), icon=icon, show=
True)
466 def set_status(self, text_value: str | list[str], b_warning: bool =
False, i_timeout: Optional[int] =
None, b_highlight: bool =
False, b_thread: bool =
False) ->
None:
468 @brief Logs a status message to status bar (with timer) and logging handler
469 @param text_value : text to set
470 @param b_warning : [True] Text is a warning; [False] normal info
471 @param b_highlight : [True] highlight text; [False] normal text
472 @param i_timeout : timeout for statustext in "ms". If None use default time
473 @param b_thread : call from thread to prevent timer activations (not allowed from thread)
475 text = self.
model.c_language.get_language_text(text_value)
if isinstance(text_value, list)
else text_value
482 locked_text = self.
model.c_language.get_language_text([
"Window Locked!",
483 "Das Fenster ist gesperrt!"])
484 config_label(self.
statusbar_left, text=locked_text, bg=LOCKED_STYLE)
487 if hasattr(self,
"status_timer"):
489 if i_timeout
is None:
491 i_timeout = STATUS_WARNING_TEXT_TIME
493 i_timeout = STATUS_HIGHLIGHT_TEXT_TIME
if b_highlight
else STATUS_TEXT_TIME
494 if i_timeout
is None:
500 background = WARNING_STYLE
if self.
model.c_monitor.is_light_theme()
else WARNING_STYLE_DARK
503 foreground = HIGHLIGHT_STYLE
if b_highlight
else None
504 background = DEFAULT_STYLE
506 config_label(self.
statusbar_left, text=text, bg=background, fg=foreground)
510 @brief Clear status bar text and set active user as default
511 @param b_override : status if actual status should override
515 user = self.
model.c_auth.s_login_user
516 if user
and user[0] == EUser.B.name:
517 user_name = self.
model.c_config.get_user_name(user)
519 user_name = f
" ({user_name})"
523 user = user.replace(
"\n",
" ")
524 s_user_text = f
"{self.model.c_language.get_language_text(L_MENU_USER)}: {user}{user_name}"
525 config_label(self.
statusbar_left, text=s_user_text, bg=DEFAULT_STYLE)
530 @brief Open report dialog.
531 @param b_clear_report : [True] create report and clear log; [False] show only status
532 @param b_auto_print : status if report was printed automatic
534 show_report_dialog(self, b_clear_report, b_auto_print)
538 @brief Update item buttons.
540 log.debug(
"Update item buttons")
543 if i < self.
model.c_config.item_number_size:
544 if self.
model.c_auth.check_user_login(L_CONFIG_USER):
546 config_btn(btn, enable=
False, show=
True, text=str(i_pos))
548 s_item_name = self.
model.c_config.get_item_button_name(i_pos)
549 s_visible_user = self.
model.c_config.get_item_visible_user(i_pos)
550 if (s_item_name !=
"")
and ((s_visible_user
is None)
or self.
model.c_auth.check_user_login(s_visible_user)):
551 if self.
model.b_show_price:
552 f_item_price = self.
model.c_config.get_item_price(i_pos)
553 s_item_name += f
"\n{f_item_price:.2f} {S_UNIT_SYMBOL}"
554 config_btn(btn, enable=
True, show=
True, text=s_item_name)
556 config_btn(btn, show=
False)
557 if self.
model.c_report.l_marked_items[i]:
558 background_color =
"orange"
559 elif self.
model.c_auth.check_user_login(EUser.FREE):
560 background_color =
"lightgreen" if self.
model.c_monitor.is_light_theme()
else "darkgreen"
562 select_bg = self.
model.c_config.get_item_background(i_pos)
564 background_color = select_bg
566 background_color =
None
567 if background_color
is None:
568 config_btn(btn, style=self.
model.c_monitor.default_button_stylesheet)
570 config_btn(btn, bg=background_color)
572 config_btn(btn, show=
False)
573 config_btn(self.
btn_lock, enable=
True)
577 @brief Update login buttons.
579 log.debug(
"Update login buttons")
581 if i < ItemNumber.MIN:
582 pressed_button = L_LOGIN_KEYS[i]
583 s_name = pressed_button
584 if not pressed_button.isdigit():
585 user_name = self.
model.c_config.get_user_name(L_LOGIN_KEYS[i])
587 s_name =
"\n".join([s_name +
":"] + user_name.split())
588 config_btn(btn, enable=
True, show=
True, text=s_name)
589 background_color =
None
590 match pressed_button:
592 background_color =
"red"
594 background_color =
"brown"
596 background_color =
"green"
598 background_color =
"blue"
600 background_color =
"lightgray" if self.
model.c_monitor.is_light_theme()
else "#141414"
602 if pressed_button[0] == EUser.B:
603 background_color =
"lightblue" if self.
model.c_monitor.is_light_theme()
else "darkblue"
604 if background_color
is None:
605 config_btn(btn, style=self.
model.c_monitor.default_button_stylesheet)
607 config_btn(btn, bg=background_color)
610 pw = self.
model.c_config.get_user_pw(pressed_button)
611 b_enable_user = bool((pressed_button
in self.
model.c_config.d_user)
612 and (S_PW
in self.
model.c_config.d_user[pressed_button])
613 and (pw.isdigit()
or pw ==
""))
614 match pressed_button:
617 case EUser.HOST | EUser.FREE | EUser.LOCAL:
620 b_enable_user =
False
622 if pressed_button[0] == EUser.B:
625 b_enable_user = bool(self.
model.c_auth.s_login_user
is not None)
626 config_btn(btn, enable=b_enable_user)
628 config_btn(btn, show=
False)
629 config_btn(self.
btn_lock, enable=
False)
634 @brief Update number buttons.
636 log.debug(
"Update number buttons")
638 if i < ItemNumber.MIN:
639 s_name = L_LOGIN_KEYS[i]
643 b_show = bool(self.
model.c_auth.e_view == EWindowView.CALC)
645 s_name = S_DOUBLE_NULL
648 b_show = bool((len(s_name) == 1)
and s_name.isnumeric())
649 b_enable = bool((s_name
not in L_SPECIAL_BTNS)
650 or ((self.
model.c_calc.s_text_total !=
"")
and ((s_name != S_DOT)
or (S_DOT
not in self.
model.c_calc.s_text_total))))
651 config_btn(btn, text=s_name, enable=b_enable, style=self.
model.c_monitor.default_button_stylesheet, show=b_show)
653 config_btn(btn, show=
False)
654 config_btn(self.
btn_lock, enable=
True)
655 match self.
model.c_auth.e_view:
656 case EWindowView.MULTI:
657 config_btn(self.
btn_multi, enable=
True, show=
True)
658 config_btn(self.
btn_calc, enable=
False, show=
False)
659 case EWindowView.CALC:
660 config_btn(self.
btn_multi, enable=
False, show=
False)
661 config_btn(self.
btn_calc, enable=
True, show=
True)
663 self.
set_status(
"Invalid Number Button Code",
True)
668 @brief Update table and print/clear button.
670 log.debug(
"Update table")
671 self.
model.c_auth.refresh_timer()
672 clear_text = self.
model.c_language.get_language_text(L_CLEAR)
673 l_row_description = self.
model.c_language.get_table_row_description()
674 b_items_in_list =
False
675 b_update_clear_btn =
True
677 config_table(table, single_row_selection=
not self.
model.c_calc.b_hold_last_print,
678 row_description=l_row_description,
679 font=self.
model.c_monitor.table_font, auto_size=
True)
680 if self.
model.c_calc.b_hold_last_print:
681 if self.
model.c_auth.e_view == EWindowView.CALC:
682 b_clear_btn_enable = bool((self.
model.c_calc.s_text_total !=
"")
or (self.
model.c_calc.i_multi > 0))
683 fg_color =
"red" if b_clear_btn_enable
else "grey"
684 config_btn(self.
btn_clear, enable=b_clear_btn_enable, text=clear_text, fg=fg_color, icon=
"")
685 background_color =
"lightgreen" if self.
model.c_monitor.is_light_theme()
else "darkgreen"
686 config_text(self.
text_total, bg=background_color, fg=
"black")
687 self.
model.c_calc.b_hold_last_print =
False
691 for i_item_pos, entry
in enumerate(self.
model.c_report.l_bar_items):
692 self.
model.c_report.l_table_position[i_item_pos] =
None
694 b_items_in_list =
True
696 item_name = self.
model.c_config.get_item_name(i_item_pos + 1)
697 if self.
model.c_auth.check_user_login(EUser.FREE):
700 f_item_price = self.
model.c_config.get_item_price(i_item_pos + 1)
701 f_price = f_item_price * entry
703 bg =
"orange" if self.
model.c_report.l_marked_items[i_item_pos]
else None
704 insert_table_item(table, i, [str(entry),
706 f
"{f_price:.2f} {S_UNIT_SYMBOL}"],
707 font=self.
model.c_monitor.table_font,
710 self.
model.c_report.l_table_position[i_item_pos] = i
713 config_table(table, row_count=i, auto_size=
True)
715 if self.
model.c_auth.b_login_state
and (self.
model.c_auth.e_view == EWindowView.MAIN):
716 self.
model.c_calc.s_text_total = f
"{f_total:.2f} {S_UNIT_SYMBOL}"
717 if self.
model.c_monitor.is_light_theme():
718 config_text(self.
text_total, bg=
"white", fg=
"black")
720 config_text(self.
text_total, bg=
"black", fg=
"white")
722 config_table(table, clear_selection=
True)
725 open_text = self.
model.c_language.get_language_text(L_OPEN)
726 b_items_in_list = b_items_in_list
or (sum(self.
model.c_report.l_bar_items) != 0)
727 if self.
model.c_printer.b_select_com_port_available:
728 if b_items_in_list
or not self.
model.c_auth.b_login_state:
729 s_btn_print_text = self.
model.c_language.get_language_text(L_PRINT)
731 s_btn_print_text = open_text
733 s_btn_print_text = self.
model.c_language.get_language_text(L_SAVE)
736 if self.
model.c_calc.i_multi > 1:
737 text = f
"{self.model.c_calc.i_multi} x"
738 elif self.
model.c_calc.f_back
is not None:
739 to_pay_text = self.
model.c_language.get_language_text([
"to pay",
"zu zahlen"])
740 given_text = self.
model.c_language.get_language_text([
"Given",
"Gegeben"])
741 s_given_sum = f
"{float(self.model.c_calc.f_payed):.2f}"
742 back_text = self.
model.c_language.get_language_text([
"Back",
"Zurück"])
743 s_back_sum = f
"{self.model.c_calc.f_back:.2f}" if (self.
model.c_calc.f_back >= 0)
else "---"
744 text = f
"{to_pay_text}: {self.model.c_calc.f_total:.2f} {S_UNIT_SYMBOL}"
745 text += f
"\n{given_text}: {s_given_sum} {S_UNIT_SYMBOL}"
746 text += f
"\n{back_text}: {s_back_sum} {S_UNIT_SYMBOL}"
747 self.
model.c_calc.f_back =
None
748 self.
model.c_calc.f_total = 0
750 text = str(self.
model.c_calc.s_text_total)
754 b_no_config_user =
not self.
model.c_auth.check_user_login(L_CONFIG_USER)
755 b_sell_user = bool(self.
model.c_auth.b_login_state
and b_no_config_user)
756 config_btn(self.
btn_multi, enable=b_sell_user)
757 config_btn(self.
btn_calc, enable=(self.
model.c_auth.check_user_login(EUser.LOCAL)
and self.
model.c_auth.b_login_state
and (self.
model.c_calc.f_total != 0)))
758 if b_update_clear_btn:
759 match self.
model.c_auth.e_view:
760 case EWindowView.CALC | EWindowView.MULTI:
761 b_clear_btn_enable = bool((self.
model.c_calc.s_text_total !=
"")
or (self.
model.c_calc.i_multi > 0))
763 b_clear_btn_enable = bool(b_items_in_list
or ((
not self.
model.c_auth.b_login_state)
and (self.
model.c_calc.s_text_total !=
"")))
764 fg_color =
"red" if b_clear_btn_enable
else "grey"
765 config_btn(self.
btn_clear, enable=b_clear_btn_enable, text=clear_text, fg=fg_color, icon=
"")
766 b_enable_print = bool((
not self.
model.c_auth.check_user_login(EUser.USER))
767 and (b_items_in_list
or ((s_btn_print_text == open_text)
and self.
model.c_auth.b_login_state
and not self.
model.c_auth.check_user_login(EUser.B))))
768 config_btn(self.
btn_print, enable=b_enable_print, text=s_btn_print_text)
769 if self.
model.c_auth.e_view == EWindowView.MAIN:
771 config_btn(self.
btn_calc, show=
True)
774 self.
model.c_monitor.resize_window(b_update_only_total_font=
True)
780 b_admin_settings = bool(self.
model.c_auth.b_login_state
and (self.
model.c_auth.check_user_login(EUser.ADMIN)))
781 b_printer_available = bool(self.
model.c_printer.s_select_com_port
is not None)
782 b_edit_user = self.
model.c_auth.b_login_state
and (self.
model.c_auth.check_user_login(L_CONFIG_USER))
783 b_log_exist = self.
model.c_report.check_sales_exist()
784 b_print_menu = (self.
model.b_enable_report
or self.
model.c_auth.check_user_login(EUser.ADMIN))
and self.
model.c_auth.b_login_state
and (
not self.
model.c_auth.check_user_login(EUser.FREE))
789 config_menu(self.
action_print_file, enable=b_printer_available, show=b_edit_user)
809 config_menu(self.
action_reset, show=self.
model.c_auth.check_user_login(EUser.ADMIN))
813 @brief Update complete screen.
815 log.debug(
"Update screen")
817 self.
model.c_monitor.check_for_style_change()
818 if self.
model.c_auth.b_login_state:
819 match self.
model.c_auth.e_view:
820 case EWindowView.MULTI | EWindowView.CALC:
825 self.
model.c_report.clear_print_items()
829 if self.
model.c_config.item_number_size != ItemNumber.MIN:
830 self.
model.c_monitor.resize_window()
832 log.debug(
"Screen is locked - no update")
836 @brief Handle item button/login (1-30) clicked.
837 @param i_item_number : button index that was clicked
839 log.debug(
"Item button %s clicked", i_item_number)
840 if self.
model.c_auth.b_login_state:
842 match self.
model.c_auth.e_view:
843 case EWindowView.MULTI | EWindowView.CALC:
844 s_name = L_LOGIN_KEYS[i_item_number]
849 s_name = S_DOUBLE_NULL
851 if (len(s_name) == 1)
and s_name.isnumeric():
854 self.
set_status(
"Invalid Multiple Button pressed",
True)
856 self.
model.c_calc.s_text_total += s_name
860 if self.
model.c_calc.i_multi > 0:
861 i_factor = self.
model.c_calc.i_multi
862 self.
model.c_calc.i_multi = 0
866 i_item_index = i_item_number
867 self.
model.c_report.l_bar_items[i_item_index] += i_factor
868 self.
model.c_calc.f_total = 0
869 if self.
model.c_sound.b_sound:
870 self.
model.c_sound.c_sound_touch.play()
871 if self.
model.c_auth.e_view == EWindowView.CALC:
872 self.
model.c_calc.b_hold_last_print =
True
875 s_key = L_LOGIN_KEYS[i_item_number]
877 if self.
model.c_auth.s_login_user
is None:
878 self.
set_status(
"None user input password",
True)
880 self.
model.c_auth.s_pw += s_key
881 pw = self.
model.c_config.get_user_pw(self.
model.c_auth.s_login_user)
882 if len(self.
model.c_auth.s_pw) == len(pw):
883 if self.
model.c_auth.s_pw == pw:
884 self.
model.c_auth.log_in()
886 self.
model.c_calc.s_text_total = self.
model.c_auth.s_login_user
887 self.
model.c_auth.s_pw =
""
889 self.
model.c_calc.s_text_total =
"*" * len(self.
model.c_auth.s_pw)
891 self.
model.c_auth.s_pw =
""
892 self.
model.c_auth.s_login_user = s_key
893 self.
model.c_calc.s_text_total = s_key
894 pw = self.
model.c_config.get_user_pw(s_key)
896 self.
model.c_auth.log_in()
897 self.
model.c_calc.f_total = 0
898 if self.
model.c_sound.b_sound:
899 if self.
model.c_auth.b_login_state:
900 self.
model.c_sound.c_sound_unlock.play()
902 self.
model.c_sound.c_sound_touch.play()
908 @brief Handle item button/login (1-30) pressed.
909 @param i_item_number : button index that was pressed
911 log.debug(
"Item button %s pressed", i_item_number)
912 if self.
model.c_auth.b_login_state
and (self.
model.c_auth.e_view == EWindowView.MAIN):
913 self.
btn_timer.start(I_BTN_LONG_PRESS_TIME)
918 @brief Handle item button/login (1-30) released.
919 @param i_item_number : button index that was released
921 log.debug(
"Item button %s released", i_item_number)
926 @brief Item button held.
928 log.debug(
"Item held")
935 @brief Handle multi button clicked.
937 log.debug(
"Multi button clicked")
938 match self.
model.c_auth.e_view:
939 case EWindowView.MULTI:
940 if self.
model.c_calc.s_text_total ==
"":
941 self.
model.c_calc.s_text_total = str(0)
942 self.
model.c_calc.i_multi = int(self.
model.c_calc.s_text_total)
943 self.
model.c_auth.e_view = EWindowView.MAIN
945 self.
model.c_calc.s_text_total =
""
946 self.
model.c_calc.i_multi = 0
947 self.
model.c_auth.e_view = EWindowView.MULTI
948 if self.
model.c_sound.b_sound:
949 self.
model.c_sound.c_sound_touch.play()
954 @brief Handle calc button clicked.
956 log.debug(
"Calc button clicked")
957 b_last_calc = bool(self.
model.c_auth.e_view == EWindowView.CALC)
958 match self.
model.c_auth.e_view:
959 case EWindowView.CALC:
960 if self.
model.c_calc.s_text_total ==
"":
961 self.
model.c_calc.s_text_total = str(0)
962 self.
model.c_calc.f_payed = float(self.
model.c_calc.s_text_total)
963 if self.
model.c_calc.f_total > self.
model.c_calc.f_payed:
964 self.
set_status([
"Not enough Money",
"Nicht genug Geld"], b_highlight=
True)
965 self.
model.c_calc.f_back = self.
model.c_calc.f_payed - self.
model.c_calc.f_total
966 self.
model.c_auth.e_view = EWindowView.MAIN
968 self.
model.c_calc.s_text_total =
""
969 self.
model.c_calc.f_back =
None
970 self.
model.c_auth.e_view = EWindowView.CALC
971 if self.
model.c_sound.b_sound:
972 self.
model.c_sound.c_sound_touch.play()
973 self.
model.c_calc.b_hold_last_print =
True
976 config_btn(self.
btn_clear, enable=
False, text=self.
model.c_language.get_language_text(L_CLEAR), fg=
"grey", icon=
"")
980 @brief Clear articles.
983 selected_row = get_selected_table_row(self.
table_items)
984 selected_column = get_selected_table_column(self.
table_items)
985 if selected_row
is not None:
986 for i_pos, table_pos
in enumerate(self.
model.c_report.l_table_position):
987 if table_pos == selected_row:
988 i_select_item = i_pos
990 if i_select_item
is None:
991 self.
model.c_report.clear_print_items()
993 if self.
model.c_report.l_bar_items[i_select_item] != 0:
994 if selected_column == 2:
995 self.
model.c_report.l_bar_items[i_select_item] = 0
997 self.
model.c_report.l_bar_items[i_select_item] -= 1
999 self.
set_status(f
"Not possible to delete single item at position {selected_row}",
True)
1003 @brief Handle clear button clicked.
1005 log.debug(
"Clear button clicked")
1006 b_ec_pressed =
False
1007 if self.
model.c_auth.b_login_state:
1008 match self.
model.c_auth.e_view:
1009 case EWindowView.MULTI | EWindowView.CALC:
1010 self.
model.c_calc.s_text_total =
""
1013 if self.
model.c_language.get_language_text(L_CLEAR) == btn_text:
1018 if self.
model.c_auth.s_pw !=
"":
1019 self.
model.c_auth.s_pw =
""
1020 if self.
model.c_auth.s_login_user
is not None:
1021 self.
model.c_calc.s_text_total = self.
model.c_auth.s_login_user
1023 self.
model.c_calc.s_text_total =
""
1024 self.
set_status(
"None user input password",
True)
1026 self.
model.c_auth.s_login_user =
None
1027 self.
model.c_calc.s_text_total =
""
1029 if self.
model.c_sound.b_sound:
1030 sound = self.
model.c_sound.c_sound_touch
if b_ec_pressed
else self.
model.c_sound.c_sound_clear
1032 if b_ec_pressed
or (self.
model.c_auth.e_view == EWindowView.CALC):
1033 self.
model.c_calc.b_hold_last_print =
True
1038 @brief Print selected articles.
1039 @param l_item_log : add printed articles to this list
1040 @param actual_datetime : date for printed items
1044 for i_item_pos, entry
in enumerate(self.
model.c_report.l_bar_items):
1046 i_item_number = i_item_pos + 1
1047 name = self.
model.c_config.get_item_name(i_item_number)
1048 f_single_price = self.
model.c_config.get_item_price(i_item_number)
1049 group = self.
model.c_config.get_item_group(i_item_number)
1050 number = i_item_number
1051 b_print = self.
model.c_config.get_print_article_status()
1053 b_print = self.
model.c_config.get_item_print_status(i_item_pos + 1)
1054 b_free_user = self.
model.c_auth.check_user_login(EUser.FREE)
1055 f_price = 0.0
if b_free_user
else f_single_price * entry
1056 f_total_price += f_price
1057 l_item_log.append(create_item(entry, name, number, group, f_single_price, f_price, self.
model.c_auth.s_login_user, actual_datetime, b_print))
1058 return f_total_price
1062 @brief Handle print button clicked.
1063 @param b_smartcard : status if print triggered by smart card
1065 log.debug(
"Print button clicked")
1067 if btn_text != self.
model.c_language.get_language_text(L_OPEN):
1068 if self.
model.c_printer.b_select_com_port_available
or self.
model.c_printer.s_select_com_port
is None:
1069 i_user_pos = self.
model.c_auth.get_user_position()
1070 if i_user_pos
is not None:
1071 i_number_of_items = sum(self.
model.c_report.l_bar_items)
1072 if i_number_of_items != 0:
1073 if i_number_of_items <= I_MAX_ITEMS_TO_PRINT:
1074 l_item_log: list[Item] = []
1075 now = datetime.now()
1076 self.
model.c_calc.f_total = 0.0
1080 if self.
model.c_config.get_auto_open()
and (self.
model.c_auth.check_user_login(EUser.LOCAL)):
1081 self.
model.c_printer.open_drawer()
1082 self.
model.c_printer.add_to_queue(l_item_log, i_user_pos, self.
model.c_calc.f_total)
1083 if self.
model.c_sound.b_sound:
1084 self.
model.c_sound.c_sound_print.play()
1085 background_color =
"lightgreen" if self.
model.c_monitor.is_light_theme()
else "darkgreen"
1086 config_text(self.
text_total, bg=background_color, fg=
"black")
1088 self.
model.c_auth.s_login_user = EUser.USER.value
1089 elif self.
model.c_auth.check_user_login(EUser.B):
1090 self.
model.c_auth.log_out()
1091 elif self.
model.c_auth.check_user_login(EUser.FREE):
1092 if self.
model.c_config.get_free_auto_logout():
1093 self.
model.c_auth.log_out()
1094 self.
model.c_calc.b_hold_last_print =
True
1095 self.
model.c_report.clear_print_items()
1097 self.
set_status([f
"Maximal {I_MAX_ITEMS_TO_PRINT} articles allowed to print",
1098 f
"Maximal {I_MAX_ITEMS_TO_PRINT} Artikel möglich zu drucken"], b_highlight=
True)
1100 self.
set_status([
"No Items to print",
"Keine Artikel zu drucken"],
True)
1103 self.
set_status([f
"No Print: {self.model.c_printer.s_select_com_port} not available",
1104 f
"Kein Druck: {self.model.c_printer.s_select_com_port} nicht verfügbar"],
True)
1106 if not self.
model.c_auth.check_user_login(EUser.B):
1107 self.
model.c_printer.open_drawer()
1108 self.
set_status([
"Open cash drawer",
"Öffne Kassenschublade"])
1110 self.
set_status(
"Stuff cant open cash drawer",
True)
1111 if self.
model.c_sound.b_sound:
1112 self.
model.c_sound.c_sound_touch.play()
1116 @brief Handle lock button clicked.
1117 @param b_auto_logout : [True] automatic logout (timeout); [False] manual logout
1120 log.debug(
"Auto logout")
1122 log.debug(
"Lock button clicked")
1123 if self.
model.c_sound.b_sound:
1124 self.
model.c_sound.c_sound_touch.play()
1125 self.
model.c_calc.s_text_total =
""
1126 self.
model.c_auth.log_out()
1131 @brief Handle report or status button clicked.
1132 @param b_clear_report : [True] create report and clear log; [False] show only status
1134 b_call_report =
True
1136 b_call_report = self.
confirm_dialog([
"Create Report & clear",
"Bericht erstellen & löschen"], s_icon_path=ICON_REPORT_LIGHT)
1138 self.
set_status([
"Show Status",
"Zwischenstand anzeigen"])
1139 self.
model.c_report.create_report(b_clear_report=b_clear_report, d_item=self.
model.c_config.d_item, b_open_folder=B_NOTEPAD_REPORT)
1141 if B_NOTEPAD_REPORT:
1142 with open(S_REPORT_TEMP_FILE, mode=
"w", encoding=
"utf-8")
as file:
1143 file.write(self.
model.c_report.s_report_text)
1144 self.
model.notepad_worker.open_report_file(S_REPORT_TEMP_FILE, ENotepadSelection.STATUS, self.
unblock_ui)
1146 if self.
model.c_printer.s_select_com_port
is not None:
1147 if b_clear_report
and (
not self.
model.c_auth.check_user_login(EUser.ADMIN)):
1148 b_print_report =
True
1150 if B_NOTEPAD_REPORT:
1151 b_print_report = self.
confirm_dialog([
"Print out status",
"Status drucken"], s_icon_path=ICON_PRINTER_LIGHT)
1153 b_print_report =
False
1155 self.
model.c_printer.add_to_report(self.
model.c_report.s_report_text)
1157 b_print_report =
False
1159 if not B_NOTEPAD_REPORT:
1166 @brief Handle change user configuration.
1167 @param e_config_selection : mode of user change (edit/import/export/reset)
1169 match e_config_selection:
1170 case EConfigSelection.RESET | EConfigSelection.EDIT:
1171 if e_config_selection == EConfigSelection.RESET:
1172 d_user_dict = D_DEFAULT_USER.copy()
1173 d_user_dict[EUser.ADMIN.value][S_PW] = DEFAULT_CODE
1174 b_reset = self.
confirm_dialog([
"Reset user",
"Benutzer zurücksetzen"], s_icon_path=ICON_USER_RESET_LIGHT)
1176 d_user_dict = self.
model.c_config.d_user.copy()
1179 if not self.
model.c_auth.check_user_login(EUser.ADMIN):
1180 if EUser.ADMIN.value
in d_user_dict:
1181 del d_user_dict[EUser.ADMIN.value]
1183 self.
set_status([
"No Admin user in config data",
1184 "Kein Admin Nutzer in den Konfigurationsdaten hinterlegt"],
True)
1185 write_config_to_file(S_USER_TEMP_FILE, d_user_dict)
1188 case EConfigSelection.IMPORT:
1189 l_title = [
"Select user configuration file to import",
"Wähle eine Benutzer Konfigurationsdatei zum importieren"]
1190 config_import_file = open_file(parent=self, title=self.
model.c_language.get_language_text(l_title),
1191 directory=self.
model.get_last_path(), filetypes=INI_FILE_TYPES)
1192 if isinstance(config_import_file, str):
1193 if config_import_file:
1194 self.
model.set_last_path(os.path.dirname(config_import_file))
1195 b_imported = self.
model.c_config.read_user_file(config_import_file)
1197 self.
set_status([f
"User configuration imported: {config_import_file}",
1198 f
"Benutzer Konfigurationsdaten importiert: {config_import_file}"])
1200 self.
set_status(
"Config import file is not string",
True)
1201 case EConfigSelection.EXPORT:
1202 l_title = [
"Export user configuration file",
"Benutzer Konfigurationsdatei exportieren"]
1203 config_export_file = save_file(parent=self, title=self.
model.c_language.get_language_text(l_title),
1204 directory=f
"{self.model.get_last_path()}/{S_USER_FILE}",
1205 filetypes=INI_FILE_TYPES)
1206 if config_export_file:
1207 self.
model.set_last_path(os.path.dirname(config_export_file))
1208 write_config_to_file(config_export_file, self.
model.c_config.d_user)
1209 self.
set_status([f
"User configuration exported: {config_export_file}",
1210 f
"Benutzer Konfigurationsdaten exportiert: {config_export_file}"])
1212 self.
set_status(f
"Invalid user menu: {e_config_selection}",
True)
1216 @brief Open user file in notepad.
1217 @param s_file_name : file name to open in notepad
1219 self.
model.notepad_worker.open_report_file(s_file_name, ENotepadSelection.USER,
lambda: self.
model.c_config.read_user_file(s_file_name,
True))
1223 @brief Handle change articles configuration.
1224 @param e_config_selection : mode of articles change (edit/import/export/reset)
1226 match e_config_selection:
1227 case EConfigSelection.RESET | EConfigSelection.EDIT:
1228 if e_config_selection == EConfigSelection.RESET:
1229 s_deposit_name =
None
1230 d_articles_dict = get_default_item_config(s_deposit_name)
1231 b_reset = self.
confirm_dialog([
"Reset articles",
"Artikel zurücksetzen"], s_icon_path=ICON_ARTICLES_RESET_LIGHT)
1233 d_articles_dict = self.
model.c_config.d_item
1236 write_config_to_file(S_ITEM_TEMP_FILE, d_articles_dict)
1239 case EConfigSelection.IMPORT:
1240 l_title = [
"Select article configuration file to import",
"Wähle eine Artikel Konfigurationsdatei zum importieren"]
1241 config_import_file = open_file(parent=self, title=self.
model.c_language.get_language_text(l_title),
1242 directory=self.
model.get_last_path(), filetypes=INI_FILE_TYPES)
1243 if isinstance(config_import_file, str):
1244 if config_import_file:
1245 self.
model.set_last_path(os.path.dirname(config_import_file))
1246 b_imported = self.
model.c_config.read_item_file(config_import_file)
1248 self.
set_status([f
"Articles configuration imported: {config_import_file}",
1249 f
"Artikel Konfigurationsdaten importiert: {config_import_file}"])
1251 self.
set_status(
"Config import file is not string",
True)
1252 case EConfigSelection.EXPORT:
1253 l_title = [
"Export article configuration file",
"Artikel Konfigurationsdatei exportieren"]
1254 config_export_file = save_file(parent=self, title=self.
model.c_language.get_language_text(l_title),
1255 directory=f
"{self.model.get_last_path()}/{S_ITEM_FILE}",
1256 filetypes=INI_FILE_TYPES)
1257 if config_export_file:
1258 self.
model.set_last_path(os.path.dirname(config_export_file))
1259 write_config_to_file(config_export_file, self.
model.c_config.d_item)
1260 self.
set_status([f
"Articles configuration exported: {config_export_file}",
1261 f
"Artikel Konfigurationsdaten exportiert: {config_export_file}"])
1262 case EConfigSelection.PDF_IMPORT:
1265 self.
set_status(f
"Invalid articles menu: {e_config_selection}",
True)
1269 @brief Open item file in notepad.
1270 @param s_file_name : file name to open in notepad
1272 self.
model.notepad_worker.open_report_file(s_file_name, ENotepadSelection.ARTICLES,
lambda: self.
model.c_config.read_item_file(s_file_name,
True))
1276 @brief Handle Open output folder.
1278 path = self.
model.s_output_path
1279 if os.path.exists(path):
1280 log.debug(
"Open Folder: %s", path)
1281 self.
set_status([f
"Open Folder: {path}", f
"Öffne Ordner: {path}"])
1282 with subprocess.Popen(
"explorer " + path.replace(
"/",
"\\")):
1285 self.
set_status([f
"Folder not exist: {path}", f
"Ordner existiert nicht: {path}"],
True)
1289 @brief Combine Reports manual to create single report from multiple other data.
1291 l_title = [
"Select report files to combine",
"Wähle Berichte zum kombinieren"]
1292 l_select_files = open_file(parent=self, title=self.
model.c_language.get_language_text(l_title),
1293 directory=self.
model.get_last_path(), filetypes=LOG_FILE_TYPES,
1295 if isinstance(l_select_files, list):
1297 self.
model.set_last_path(os.path.dirname(l_select_files[0]))
1298 self.
model.c_report.create_report(l_files=l_select_files, b_combine=
True, b_clear_report=
False,
1299 s_path=self.
model.get_last_path(), b_open_folder=
True)
1301 self.
set_status(
"Combine files are not list",
True)
1305 @brief Combine Reports manual to create single report from multiple other data.
1307 l_title = [
"Select directory to combine",
"Wähle ein Verzeichnis zum kombinieren"]
1308 directory = open_directory(parent=self, title=self.
model.c_language.get_language_text(l_title), directory=self.
model.get_last_path())
1310 self.
model.set_last_path(directory)
1313 for root, _dirs, files
in os.walk(directory):
1315 if file.endswith(
".csv"):
1316 l_files.append(os.path.join(root, file))
1319 self.
model.c_report.create_report(l_files=l_files, b_combine=
True, b_clear_report=
False,
1320 s_path=self.
model.get_last_path(), b_open_folder=
True)
1322 self.
set_status([
"No CSV files found",
"Keine CSV Dateien gefunden"])
1326 @brief Select and print markdown file from directory.
1328 if self.
model.c_printer.s_select_com_port
is not None:
1329 l_title = [
"Select file to print",
"Datei zum Drucken auswählen"]
1330 select_file = open_file(parent=self, title=self.
model.c_language.get_language_text(l_title),
1331 directory=self.
model.get_last_path(), filetypes=REPORT_FILE_TYPES)
1332 if isinstance(select_file, str):
1334 with open(select_file, mode=
"r", encoding=
"utf-8")
as file:
1336 self.
model.c_printer.add_to_report(text)
1337 self.
set_status([f
"Print file: {select_file}", f
"Datei wird gedruckt: {select_file}"])
1339 self.
set_status(
"Print file is not string",
True)
1341 self.
set_status([
"File cannot be printed. Printer not available.",
1342 "Datei kann nicht gedruckt werden. Drucker nicht verfügbar."], b_warning=
True)
1346 @brief Article Print Preview
1350 i_break_line = int(self.
model.c_printer.i_line_break / 2)
1351 line_text =
"Name".ljust(i_break_line) +
" | Group | Price "
1352 line_text +=
"| Cut"
1353 l_preview_text.append(line_text)
1354 line_text =
"-" * (i_break_line + 50)
1355 l_preview_text.append(line_text)
1357 for i_item_pos
in range(I_ITEM_ARRAY_SIZE):
1358 i_item_number = i_item_pos + 1
1359 name = self.
model.c_config.get_item_name(i_item_number)
1361 if name !=
"" and b_show:
1362 group = self.
model.c_config.get_item_group(i_item_number)
1363 f_item_price = self.
model.c_config.get_item_price(i_item_number)
1364 s_item_price = f
"{f_item_price:.2f} {S_UNIT_SYMBOL}"
1365 s_item_price = s_item_price.rjust(8)
1366 line_text = f
"{name.ljust(i_break_line)[:i_break_line]}"
1367 line_text += f
" | {group}"
1368 line_text += f
" |{s_item_price}"
1369 line_text += f
"| {name[i_break_line:]}"
1370 l_preview_text.append(line_text)
1372 preview_text =
'\n'.join(l_preview_text)
1373 show_report_dialog(self, report_text=preview_text)
1378 @brief Handle change output path.
1380 l_title = [
"Set output path",
"Wähle ein Ausgabeverzeichnis"]
1381 s_path = open_directory(parent=self, title=self.
model.c_language.get_language_text(l_title), directory=self.
model.s_output_path)
1383 self.
model.set_last_path(s_path)
1384 self.
model.update_output_path(s_path)
1388 @brief Handle reset configuration.
1390 log.debug(
"Reset clicked")
1391 b_reset, ok = reset_config_dialog(self)
1396 self.
set_status([
"Invalid Password",
"Passwort ungültig"])
Mode of configuration change.
The view-controller for main window.
None resizeEvent(self, RESIZE_EVENT|None _event)
Default resize Event Method to handle change of window size.
None btn_item_clicked(self, int i_item_number)
Handle item button/login (1-30) clicked.
None show_number_btns(self)
Update number buttons.
None edit_user_in_notepad(self, str s_file_name)
Open user file in notepad.
None update_screen(self)
Update complete screen.
None show_update_dialog(self, str newer_tool_version)
Show Update dialog.
None show_login_btns(self)
Update login buttons.
None btn_item_held(self)
Item button held.
None open_report_dialog(self, bool b_clear_report, bool b_auto_print)
Open report dialog.
action_articles_pdf_import
None set_ui(self, bool b_state)
Blocks/Unblock the main UI elements.
None show_welcome_dialog(self)
Show welcome screen and choose admin password.
None show_item_btns(self)
Update item buttons.
None update_table(self)
Update table and print/clear button.
None btn_item_released(self, int i_item_number)
Handle item button/login (1-30) released.
None block_ui(self)
Blocks the main UI elements.
bool confirm_dialog(self, str|list[str] title_value, str s_icon_path=ICON_APP, Optional[list[str]] l_optional_text=None)
Show confirm dialog to accept or cancel.
None btn_item_pressed(self, int i_item_number)
Handle item button/login (1-30) pressed.
None clear_articles(self)
Clear articles.
None __init__(self, UncaughtHook qt_exception_hook, LogConfig log_config, bool test_mode=False, bool authenticated=False, *Any args, **Any kwargs)
None closeEvent(self, CLOSE_EVENT|None event)
Default close Event Method to handle application close.
None set_status(self, str|list[str] text_value, bool b_warning=False, Optional[int] i_timeout=None, bool b_highlight=False, bool b_thread=False)
Logs a status message to status bar (with timer) and logging handler.
float print_selected_articles(self, list[Item] l_item_log, datetime actual_datetime)
Print selected articles.
None clear_status(self, bool b_override=False)
Clear status bar text and set active user as default.
None show_unauthenticated_dialog(self)
Show unauthenticated dialog.
None update_menu(self)
Update menu.
None edit_items_in_notepad(self, str s_file_name)
Open item file in notepad.
Holds the data of the application.
None connect_menu_com_port(ACTION menu, Callable[[str], None] function)
Connect menu for COM ports.