diff --git a/docs/faq.md b/docs/faq.md index 4f4fd065..74ba1030 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -138,6 +138,18 @@ If you don't understand what docker is or what it does, don't worry. CocktailBerry will perfectly run without Docker installed. It's for some optional advanced features you can add anytime you are interested or ready for them. +### How to Exit the Program + +If you want to exit the program to get to the desktop, because you want to do some adjustments, +just press alt+F4 in the main program on your keyboard. +Like with most programs, this will exit the current opened program. +You will then be on your desktop. + +### I don't Need a Password + +Just set the password empty / delete all numbers. +If the password setting ist empty, actions requiring a password will automatically succeed without prompting a password. + ## Other ### What about Tube Volume @@ -146,7 +158,7 @@ If your pumps got a long tube to the bottle, the first cocktail may have too lit You can set the `MAKER_TUBE_VOLUME` to an approximate value which corresponds to the average of the tube volume. When applying a new bottle, CocktailBerry will also pump that much volume up. -### Whats up with LEDs +### Implementing LEDs You can define one or more pins which control a LED (array). The LEDs will light up during cocktail preparation, as well when the cocktail is finished. @@ -155,4 +167,12 @@ Instead of just turning on / off / blinking, the LED will then have some advance If you want to have multiple ring LEDs having the effect synchronously, you can define the number of identical daisy chained rings. The program will then not treat this chain as one, but as multiple chains. This does not include some default LEDs used for general lighting of the machine, because they usually don't need controlling. -It's better to directly connect them to the main source current and turn them on when the machine is turned on. \ No newline at end of file +It's better to directly connect them to the main source current and turn them on when the machine is turned on. + +### View Logs + +You can either go to the logs folder to have the raw logs. +Or you can go to the option window and select the logs option. +Then you will get a summarized view of the logs. +The latest logs are shown on top. +Identical logs are only shown once, with their latest occurrence time, as well as count. \ No newline at end of file diff --git a/docs/features.md b/docs/features.md index f4959381..8e01750f 100644 --- a/docs/features.md +++ b/docs/features.md @@ -21,6 +21,7 @@ Usually, updating to the lastest version is always a good idea. | **v1.13.1** | Clearing the database over the CLI | | **v1.14.0** | Can invert pin, set simultaneous pump count, generic board | | **v1.15.0** | Control a LED during cocktail preparation | +| **v1.16.0** | Summarized logs over the GUI | !!! abstract "And much More" This list is by no means a full list of changes. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 3a9bd9ee..d4e785ac 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -123,6 +123,11 @@ Ensure that you have unchecked the "Reserve space, and not covered by maximised You can find it under the panel preferences (right click the task bar > panel settings > Advanced). Unchecking this box usually fixes this problem. +## Reset Config + +In case you want to reset the configuration, it is the best way to just delete the custom_config.yaml in the main folder. +This file holds your configuration and will be created with the defaults if it does not exists. + ## Problems Installing Software on Raspberry Pi The Raspberry Pi can sometimes differ from other machines in terms of installation. Here are some issues that might occur. diff --git a/scripts/compile_ui_to_python.ps1 b/scripts/compile_ui_to_python.ps1 index 6ae431ce..ddea74cd 100644 --- a/scripts/compile_ui_to_python.ps1 +++ b/scripts/compile_ui_to_python.ps1 @@ -1,6 +1,6 @@ cd .\src\ui_elements\ -$files = @("available", "bonusingredient", "bottlewindow", "cocktailmanager", "calibration", "customdialog", "datepicker", "handadds", "keyboard", "optionwindow", "numpad", "progressbarwindow", "teamselection", "passworddialog", "customprompt") +$files = @("available", "bonusingredient", "bottlewindow", "cocktailmanager", "calibration", "customdialog", "datepicker", "handadds", "keyboard", "optionwindow", "numpad", "progressbarwindow", "teamselection", "passworddialog", "customprompt", "logwindow") foreach ($f in $files) { pyuic5 -x .\$f.ui -o .\$f.py diff --git a/src/dialog_handler.py b/src/dialog_handler.py index bb3c808a..64813115 100644 --- a/src/dialog_handler.py +++ b/src/dialog_handler.py @@ -66,6 +66,9 @@ def user_okay(self, text: str): def password_prompt(self): """Opens a password prompt, return if successful entered password""" from src.ui.setup_password_dialog import PasswordDialog + # if password is empty, return true + if cfg.UI_MASTERPASSWORD == "": + return True password_dialog = PasswordDialog() if password_dialog.exec_(): return True @@ -474,5 +477,9 @@ def adjust_custom_prompt(self, w): w.yes_button.setText(self.__choose_language("yes_button")) w.no_button.setText(self.__choose_language("no_button")) + def adjust_log_window(self, w): + """Translates the elements from the logs window""" + w.button_back.setText(self.__choose_language("back")) + UI_LANGUAGE = UiLanguage() diff --git a/src/language.yaml b/src/language.yaml index 0ec3c4b2..944aa4a4 100644 --- a/src/language.yaml +++ b/src/language.yaml @@ -183,6 +183,9 @@ ui: no_button: en: 'No' de: 'Nein' + back: + en: '< Back' + de: '< Zurück' # dynamic values for maker tab maker: add_self: diff --git a/src/logger_handler.py b/src/logger_handler.py index bb424e8b..bd62f2c4 100644 --- a/src/logger_handler.py +++ b/src/logger_handler.py @@ -27,7 +27,7 @@ def __init__(self, logger_name: str, filename: str = LogFiles.PRODUCTION): if not logger.hasHandlers(): file_handler = logging.FileHandler(self.path) file_handler.setLevel(logging.DEBUG) - formatter = logging.Formatter("%(asctime)s | %(name)s | %(levelname)s: %(message)s", "%Y-%m-%d %H:%M") + formatter = logging.Formatter("%(asctime)s | %(levelname)-8s | %(message)s [%(name)s]", "%Y-%m-%d %H:%M") file_handler.setFormatter(formatter) logger.addHandler(file_handler) diff --git a/src/machine/leds.py b/src/machine/leds.py index 2a5abfdb..7401f7c1 100644 --- a/src/machine/leds.py +++ b/src/machine/leds.py @@ -127,7 +127,10 @@ def __init__(self, pin: int) -> None: def _preparation_thread(self): """Fills one by one with same random color, then repeats / overwrites old ones""" - wait_ms = 40 + # Make the circle / dot approximate 2 rounds per second + wait_ms = 500 / cfg.LED_COUNT + # not faster than 10ms + wait_ms = max(10, wait_ms) self.turn_on(Color(randint(0, 255), randint(0, 255), randint(0, 255))) while self.is_preparing: color = Color( diff --git a/src/tabs/maker.py b/src/tabs/maker.py index 6b9bca0d..05c74211 100644 --- a/src/tabs/maker.py +++ b/src/tabs/maker.py @@ -18,7 +18,7 @@ from src.config_manager import shared -LOG_HANDLER = LoggerHandler("maker_module") +_LOGGER = LoggerHandler("maker_module") T = TypeVar('T', int, float) @@ -71,7 +71,7 @@ def __generate_maker_log_entry(cocktail_volume: int, cocktail_name: str, taken_t if not shared.make_cocktail: pumped_volume = round(cocktail_volume * (taken_time) / max_time) cancel_log_addition = f" - Recipe canceled at {round(taken_time, 1)} s - {pumped_volume} ml" - LOG_HANDLER.log_event("INFO", f"{volume_string:6} | {cocktail_name}{cancel_log_addition}") + _LOGGER.log_event("INFO", f"{volume_string:6} - {cocktail_name}{cancel_log_addition}") @logerror diff --git a/src/ui/setup_log_window.py b/src/ui/setup_log_window.py new file mode 100644 index 00000000..24fe60a5 --- /dev/null +++ b/src/ui/setup_log_window.py @@ -0,0 +1,95 @@ +import re +from pathlib import Path +from collections import Counter +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QMainWindow +from src.dialog_handler import UI_LANGUAGE +from src.display_controller import DP_CONTROLLER +from src.ui_elements.logwindow import Ui_LogWindow + +_DIRPATH = Path(__file__).parent.absolute() +_LOG_FOLDER = _DIRPATH.parents[1] / "logs" +_DEFAULT_SELECTED = "production_logs.log" +_DEBUG_FILE = "debuglog.log" + + +class LogWindow(QMainWindow, Ui_LogWindow): + """ Creates the log window Widget. """ + + def __init__(self): + """ Init. Connect all the buttons and set window policy. """ + super().__init__() + self.setupUi(self) + self.setWindowFlags(Qt.Window | Qt.CustomizeWindowHint | Qt.WindowStaysOnTopHint) # type: ignore + self.setAttribute(Qt.WA_DeleteOnClose) # type: ignore + DP_CONTROLLER.inject_stylesheet(self) + # Connect all the buttons, generates a list of the numbers an object names to do that + self.button_back.clicked.connect(self.close) + + # Get log file names, fill widget, select default, if it exists + self.log_files = self._get_log_files() + self.selection_logs.activated.connect(self._read_logs) + DP_CONTROLLER.fill_single_combobox(self.selection_logs, self.log_files, first_empty=False) + if _DEFAULT_SELECTED in self.log_files: + DP_CONTROLLER.set_combobox_item(self.selection_logs, _DEFAULT_SELECTED) + # activated does only trigger if changed by user, so we need to read in here + self._read_logs() + + self.move(0, 0) + UI_LANGUAGE.adjust_log_window(self) + self.showFullScreen() + DP_CONTROLLER.set_display_settings(self) + + def _get_log_files(self): + """Checks the logs folder for all existing log files""" + return [file.name for file in _LOG_FOLDER.glob("*.log")] + + def _read_logs(self): + """Read the current selected log file""" + log_name = self.selection_logs.currentText() + # Return if empty selection + if log_name == "": + return + log_path = _LOG_FOLDER / log_name + log_text = log_path.read_text() + # Handle debug logs differently, since they save error traces, + # just display the read in text from log in this case + if log_name == _DEBUG_FILE: + logs_to_render = self._parse_debug_logs(log_text) + else: + logs_to_render = self._parse_log(log_text) + self.text_display.setText(logs_to_render) + + def _parse_log(self, log_text: str): + """Parse all logs and return display object. + Needs logs from new to old, if same message was already there, skip it. + """ + data: dict[str, str] = {} + counter = Counter() + for line in log_text.splitlines()[::-1]: + date, message = self._parse_log_line(line) + if message not in data: + data[message] = date + counter[message] = 1 + else: + counter[message] += 1 + log_list_data = [f"{key} ({counter[key]}x, latest: {value})" for key, value in data.items()] + return "\n".join(log_list_data) + + def _parse_log_line(self, line: str): + """Parse the log message and return the timestamp + msg""" + parts = line.split(" | ", maxsplit=1) + parsed_date = parts[0] + # usually, we only get 2 parts, due to the maxsplit + parsed_message = " | ".join(parts[1::]) + return parsed_date, parsed_message + + def _parse_debug_logs(self, log): + """Parses and inverts the debug logs""" + # having into group returns also the matched date + # This needs to be joined before inverting. + # Also, the first value is an empty string + date_regex = r"(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})" + information_list = [x for x in re.split(date_regex, log) if x != ""] + pairs = [" ".join(information_list[i:i + 2]) for i in range(0, len(information_list), 2)] + return "\n".join(pairs[::-1]) diff --git a/src/ui/setup_option_window.py b/src/ui/setup_option_window.py index 9052209e..7a92a832 100644 --- a/src/ui/setup_option_window.py +++ b/src/ui/setup_option_window.py @@ -10,6 +10,7 @@ from PyQt5.QtWidgets import QMainWindow from src.ui.create_config_window import ConfigWindow +from src.ui.setup_log_window import LogWindow from src.ui_elements.optionwindow import Ui_Optionwindow from src.display_controller import DP_CONTROLLER from src.dialog_handler import UI_LANGUAGE @@ -51,8 +52,11 @@ def __init__(self, parent): self.button_backup.clicked.connect(self._create_backup) self.button_restore.clicked.connect(self._upload_backup) self.button_export.clicked.connect(SAVE_HANDLER.export_data) + self.button_logs.clicked.connect(self._show_logs) + self.button_rfid.clicked.connect(self._open_rfid_writer) self.config_window: Optional[ConfigWindow] = None + self.log_window: Optional[LogWindow] = None UI_LANGUAGE.adjust_option_window(self) self.showFullScreen() DP_CONTROLLER.set_display_settings(self) @@ -131,3 +135,12 @@ def _get_user_folder_response(self): if not selected_path: return None return Path(selected_path).absolute() + + def _show_logs(self): + """Opens the logs window""" + self.close() + self.log_window = LogWindow() + + def _open_rfid_writer(self): + """Opens the rfid writer window""" + # TODO: Implement the rfid logic diff --git a/src/ui/styles/_elements.scss b/src/ui/styles/_elements.scss index 1dd023fb..490fd6db 100644 --- a/src/ui/styles/_elements.scss +++ b/src/ui/styles/_elements.scss @@ -95,6 +95,9 @@ QWidget { &[cssClass~='right-shift'] { margin: 5px 20px 5px 0px; } + &:disabled { + text-decoration: line-through; + } } QTabWidget::pane { @@ -275,12 +278,19 @@ QListWidget { & QScrollBar:vertical { border-right: 0px solid $border; } + & QScrollBar:horizontal { + border-bottom: 0px solid $border; + } } QScrollArea QScrollBar:vertical { border-right: $default-element-border; } +QScrollArea QScrollBar:horizontal { + border-bottom: $default-element-border; +} + /* Moving List Widget here from the single objects */ QScrollBar:vertical { border: $default-element-border; @@ -291,12 +301,24 @@ QScrollBar:vertical { margin: 25px 0px 25px 0px; } -QScrollBar::handle:vertical { - background: $primary; - border-radius: $scrollbar-border-radius - 5px; - &:pressed { - background-color: $secondary; - border-color: $secondary; +QScrollBar:horizontal { + border: $default-element-border; + border-bottom: 1px solid $border; + border-radius: $scrollbar-border-radius; + background: $background; + height: 40px; + margin: 0px 25px 0px 25px; +} + +QScrollBar::handle { + &:vertical, + &:horizontal { + background: $primary; + border-radius: $scrollbar-border-radius - 5px; + &:pressed { + background-color: $secondary; + border-color: $secondary; + } } } @@ -311,6 +333,16 @@ QScrollBar::add-line:vertical { subcontrol-origin: margin; } +QScrollBar::add-line:horizontal { + border: $default-element-border; + border-top-right-radius: $scrollbar-border-radius; + border-bottom-right-radius: $scrollbar-border-radius; + background-color: $border; + width: 38px; + subcontrol-position: right; + subcontrol-origin: margin; +} + // top button QScrollBar::sub-line:vertical { border: $default-element-border; @@ -322,14 +354,28 @@ QScrollBar::sub-line:vertical { subcontrol-origin: margin; } +QScrollBar::sub-line:horizontal { + border: $default-element-border; + border-top-left-radius: $scrollbar-border-radius; + border-bottom-left-radius: $scrollbar-border-radius; + background-color: $border; + width: 38px; + subcontrol-position: left; + subcontrol-origin: margin; +} + // remove the grid thingy from bg QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical { +QScrollBar::sub-page:vertical, +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal { background: none; } QScrollBar::sub-line:vertical:pressed, -QScrollBar::add-line:vertical:pressed { +QScrollBar::add-line:vertical:pressed, +QScrollBar::sub-line:horizontal:pressed, +QScrollBar::add-line:horizontal:pressed { background-color: $secondary; border-color: $secondary; } diff --git a/src/ui/styles/alien.css b/src/ui/styles/alien.css index 3ab07502..744f9b17 100644 --- a/src/ui/styles/alien.css +++ b/src/ui/styles/alien.css @@ -67,6 +67,8 @@ QWidget { margin: 5px 0px 5px 20px; } QWidget[cssClass~='right-shift'] { margin: 5px 20px 5px 0px; } + QWidget:disabled { + text-decoration: line-through; } QTabWidget::pane { border: 1px solid #586c5b; @@ -211,10 +213,15 @@ QListWidget { border-top-right-radius: 21px; } QListWidget QScrollBar:vertical { border-right: 0px solid #586c5b; } + QListWidget QScrollBar:horizontal { + border-bottom: 0px solid #586c5b; } QScrollArea QScrollBar:vertical { border-right: 2px solid #586c5b; } +QScrollArea QScrollBar:horizontal { + border-bottom: 2px solid #586c5b; } + /* Moving List Widget here from the single objects */ QScrollBar:vertical { border: 2px solid #586c5b; @@ -224,10 +231,18 @@ QScrollBar:vertical { width: 40px; margin: 25px 0px 25px 0px; } -QScrollBar::handle:vertical { +QScrollBar:horizontal { + border: 2px solid #586c5b; + border-bottom: 1px solid #586c5b; + border-radius: 15px; + background: #111111; + height: 40px; + margin: 0px 25px 0px 25px; } + +QScrollBar::handle:vertical, QScrollBar::handle:horizontal { background: #00dd4a; border-radius: 10px; } - QScrollBar::handle:vertical:pressed { + QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { background-color: #00e4d1; border-color: #00e4d1; } @@ -240,6 +255,15 @@ QScrollBar::add-line:vertical { subcontrol-position: bottom; subcontrol-origin: margin; } +QScrollBar::add-line:horizontal { + border: 2px solid #586c5b; + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + background-color: #586c5b; + width: 38px; + subcontrol-position: right; + subcontrol-origin: margin; } + QScrollBar::sub-line:vertical { border: 2px solid #586c5b; border-top-left-radius: 15px; @@ -249,12 +273,25 @@ QScrollBar::sub-line:vertical { subcontrol-position: top; subcontrol-origin: margin; } +QScrollBar::sub-line:horizontal { + border: 2px solid #586c5b; + border-top-left-radius: 15px; + border-bottom-left-radius: 15px; + background-color: #586c5b; + width: 38px; + subcontrol-position: left; + subcontrol-origin: margin; } + QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical { +QScrollBar::sub-page:vertical, +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal { background: none; } QScrollBar::sub-line:vertical:pressed, -QScrollBar::add-line:vertical:pressed { +QScrollBar::add-line:vertical:pressed, +QScrollBar::sub-line:horizontal:pressed, +QScrollBar::add-line:horizontal:pressed { background-color: #00e4d1; border-color: #00e4d1; } diff --git a/src/ui/styles/bavaria.css b/src/ui/styles/bavaria.css index 5e263e69..947c91b9 100644 --- a/src/ui/styles/bavaria.css +++ b/src/ui/styles/bavaria.css @@ -67,6 +67,8 @@ QWidget { margin: 5px 0px 5px 20px; } QWidget[cssClass~='right-shift'] { margin: 5px 20px 5px 0px; } + QWidget:disabled { + text-decoration: line-through; } QTabWidget::pane { border: 1px solid #0b0e41; @@ -211,10 +213,15 @@ QListWidget { border-top-right-radius: 21px; } QListWidget QScrollBar:vertical { border-right: 0px solid #0b0e41; } + QListWidget QScrollBar:horizontal { + border-bottom: 0px solid #0b0e41; } QScrollArea QScrollBar:vertical { border-right: 2px solid #0b0e41; } +QScrollArea QScrollBar:horizontal { + border-bottom: 2px solid #0b0e41; } + /* Moving List Widget here from the single objects */ QScrollBar:vertical { border: 2px solid #0b0e41; @@ -224,10 +231,18 @@ QScrollBar:vertical { width: 40px; margin: 25px 0px 25px 0px; } -QScrollBar::handle:vertical { +QScrollBar:horizontal { + border: 2px solid #0b0e41; + border-bottom: 1px solid #0b0e41; + border-radius: 15px; + background: #dad9d9; + height: 40px; + margin: 0px 25px 0px 25px; } + +QScrollBar::handle:vertical, QScrollBar::handle:horizontal { background: #007bff; border-radius: 10px; } - QScrollBar::handle:vertical:pressed { + QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { background-color: #01276a; border-color: #01276a; } @@ -240,6 +255,15 @@ QScrollBar::add-line:vertical { subcontrol-position: bottom; subcontrol-origin: margin; } +QScrollBar::add-line:horizontal { + border: 2px solid #0b0e41; + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + background-color: #0b0e41; + width: 38px; + subcontrol-position: right; + subcontrol-origin: margin; } + QScrollBar::sub-line:vertical { border: 2px solid #0b0e41; border-top-left-radius: 15px; @@ -249,12 +273,25 @@ QScrollBar::sub-line:vertical { subcontrol-position: top; subcontrol-origin: margin; } +QScrollBar::sub-line:horizontal { + border: 2px solid #0b0e41; + border-top-left-radius: 15px; + border-bottom-left-radius: 15px; + background-color: #0b0e41; + width: 38px; + subcontrol-position: left; + subcontrol-origin: margin; } + QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical { +QScrollBar::sub-page:vertical, +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal { background: none; } QScrollBar::sub-line:vertical:pressed, -QScrollBar::add-line:vertical:pressed { +QScrollBar::add-line:vertical:pressed, +QScrollBar::sub-line:horizontal:pressed, +QScrollBar::add-line:horizontal:pressed { background-color: #01276a; border-color: #01276a; } diff --git a/src/ui/styles/berry.css b/src/ui/styles/berry.css index e14d6ed2..754fb617 100644 --- a/src/ui/styles/berry.css +++ b/src/ui/styles/berry.css @@ -67,6 +67,8 @@ QWidget { margin: 5px 0px 5px 20px; } QWidget[cssClass~='right-shift'] { margin: 5px 20px 5px 0px; } + QWidget:disabled { + text-decoration: line-through; } QTabWidget::pane { border: 1px solid #6f6f87; @@ -211,10 +213,15 @@ QListWidget { border-top-right-radius: 21px; } QListWidget QScrollBar:vertical { border-right: 0px solid #6f6f87; } + QListWidget QScrollBar:horizontal { + border-bottom: 0px solid #6f6f87; } QScrollArea QScrollBar:vertical { border-right: 2px solid #6f6f87; } +QScrollArea QScrollBar:horizontal { + border-bottom: 2px solid #6f6f87; } + /* Moving List Widget here from the single objects */ QScrollBar:vertical { border: 2px solid #6f6f87; @@ -224,10 +231,18 @@ QScrollBar:vertical { width: 40px; margin: 25px 0px 25px 0px; } -QScrollBar::handle:vertical { +QScrollBar:horizontal { + border: 2px solid #6f6f87; + border-bottom: 1px solid #6f6f87; + border-radius: 15px; + background: #0d0d0d; + height: 40px; + margin: 0px 25px 0px 25px; } + +QScrollBar::handle:vertical, QScrollBar::handle:horizontal { background: #3a4cbf; border-radius: 10px; } - QScrollBar::handle:vertical:pressed { + QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { background-color: #ec407a; border-color: #ec407a; } @@ -240,6 +255,15 @@ QScrollBar::add-line:vertical { subcontrol-position: bottom; subcontrol-origin: margin; } +QScrollBar::add-line:horizontal { + border: 2px solid #6f6f87; + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + background-color: #6f6f87; + width: 38px; + subcontrol-position: right; + subcontrol-origin: margin; } + QScrollBar::sub-line:vertical { border: 2px solid #6f6f87; border-top-left-radius: 15px; @@ -249,12 +273,25 @@ QScrollBar::sub-line:vertical { subcontrol-position: top; subcontrol-origin: margin; } +QScrollBar::sub-line:horizontal { + border: 2px solid #6f6f87; + border-top-left-radius: 15px; + border-bottom-left-radius: 15px; + background-color: #6f6f87; + width: 38px; + subcontrol-position: left; + subcontrol-origin: margin; } + QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical { +QScrollBar::sub-page:vertical, +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal { background: none; } QScrollBar::sub-line:vertical:pressed, -QScrollBar::add-line:vertical:pressed { +QScrollBar::add-line:vertical:pressed, +QScrollBar::sub-line:horizontal:pressed, +QScrollBar::add-line:horizontal:pressed { background-color: #ec407a; border-color: #ec407a; } diff --git a/src/ui/styles/default.css b/src/ui/styles/default.css index 2abdd1f2..04421fc1 100644 --- a/src/ui/styles/default.css +++ b/src/ui/styles/default.css @@ -67,6 +67,8 @@ QWidget { margin: 5px 0px 5px 20px; } QWidget[cssClass~='right-shift'] { margin: 5px 20px 5px 0px; } + QWidget:disabled { + text-decoration: line-through; } QTabWidget::pane { border: 1px solid #616161; @@ -211,10 +213,15 @@ QListWidget { border-top-right-radius: 21px; } QListWidget QScrollBar:vertical { border-right: 0px solid #616161; } + QListWidget QScrollBar:horizontal { + border-bottom: 0px solid #616161; } QScrollArea QScrollBar:vertical { border-right: 2px solid #616161; } +QScrollArea QScrollBar:horizontal { + border-bottom: 2px solid #616161; } + /* Moving List Widget here from the single objects */ QScrollBar:vertical { border: 2px solid #616161; @@ -224,10 +231,18 @@ QScrollBar:vertical { width: 40px; margin: 25px 0px 25px 0px; } -QScrollBar::handle:vertical { +QScrollBar:horizontal { + border: 2px solid #616161; + border-bottom: 1px solid #616161; + border-radius: 15px; + background: #0d0d0d; + height: 40px; + margin: 0px 25px 0px 25px; } + +QScrollBar::handle:vertical, QScrollBar::handle:horizontal { background: #007bff; border-radius: 10px; } - QScrollBar::handle:vertical:pressed { + QScrollBar::handle:vertical:pressed, QScrollBar::handle:horizontal:pressed { background-color: #ef9700; border-color: #ef9700; } @@ -240,6 +255,15 @@ QScrollBar::add-line:vertical { subcontrol-position: bottom; subcontrol-origin: margin; } +QScrollBar::add-line:horizontal { + border: 2px solid #616161; + border-top-right-radius: 15px; + border-bottom-right-radius: 15px; + background-color: #616161; + width: 38px; + subcontrol-position: right; + subcontrol-origin: margin; } + QScrollBar::sub-line:vertical { border: 2px solid #616161; border-top-left-radius: 15px; @@ -249,12 +273,25 @@ QScrollBar::sub-line:vertical { subcontrol-position: top; subcontrol-origin: margin; } +QScrollBar::sub-line:horizontal { + border: 2px solid #616161; + border-top-left-radius: 15px; + border-bottom-left-radius: 15px; + background-color: #616161; + width: 38px; + subcontrol-position: left; + subcontrol-origin: margin; } + QScrollBar::add-page:vertical, -QScrollBar::sub-page:vertical { +QScrollBar::sub-page:vertical, +QScrollBar::add-page:horizontal, +QScrollBar::sub-page:horizontal { background: none; } QScrollBar::sub-line:vertical:pressed, -QScrollBar::add-line:vertical:pressed { +QScrollBar::add-line:vertical:pressed, +QScrollBar::sub-line:horizontal:pressed, +QScrollBar::add-line:horizontal:pressed { background-color: #ef9700; border-color: #ef9700; } diff --git a/src/ui_elements/logwindow.py b/src/ui_elements/logwindow.py new file mode 100644 index 00000000..04fcd119 --- /dev/null +++ b/src/ui_elements/logwindow.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file '.\logwindow.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_LogWindow(object): + def setupUi(self, LogWindow): + LogWindow.setObjectName("LogWindow") + LogWindow.resize(800, 480) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(LogWindow.sizePolicy().hasHeightForWidth()) + LogWindow.setSizePolicy(sizePolicy) + LogWindow.setMinimumSize(QtCore.QSize(800, 480)) + LogWindow.setMaximumSize(QtCore.QSize(800, 480)) + LogWindow.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) + LogWindow.setStyleSheet("") + self.centralwidget = QtWidgets.QWidget(LogWindow) + self.centralwidget.setObjectName("centralwidget") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.button_back = QtWidgets.QPushButton(self.centralwidget) + self.button_back.setMaximumSize(QtCore.QSize(5000, 300)) + font = QtGui.QFont() + font.setPointSize(28) + font.setBold(True) + font.setWeight(75) + self.button_back.setFont(font) + self.button_back.setObjectName("button_back") + self.horizontalLayout.addWidget(self.button_back) + self.selection_logs = QtWidgets.QComboBox(self.centralwidget) + self.selection_logs.setMinimumSize(QtCore.QSize(105, 38)) + self.selection_logs.setMaximumSize(QtCore.QSize(5000, 100)) + font = QtGui.QFont() + font.setPointSize(14) + font.setBold(True) + font.setWeight(75) + self.selection_logs.setFont(font) + self.selection_logs.setMaxVisibleItems(11) + self.selection_logs.setObjectName("selection_logs") + self.horizontalLayout.addWidget(self.selection_logs) + self.verticalLayout_2.addLayout(self.horizontalLayout) + self.text_display = QtWidgets.QTextBrowser(self.centralwidget) + font = QtGui.QFont() + font.setPointSize(12) + self.text_display.setFont(font) + self.text_display.setFrameShape(QtWidgets.QFrame.NoFrame) + self.text_display.setFrameShadow(QtWidgets.QFrame.Plain) + self.text_display.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.text_display.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) + self.text_display.setObjectName("text_display") + self.verticalLayout_2.addWidget(self.text_display) + LogWindow.setCentralWidget(self.centralwidget) + + self.retranslateUi(LogWindow) + QtCore.QMetaObject.connectSlotsByName(LogWindow) + + def retranslateUi(self, LogWindow): + _translate = QtCore.QCoreApplication.translate + LogWindow.setWindowTitle(_translate("LogWindow", "Logs")) + self.button_back.setText(_translate("LogWindow", "< Back")) + self.button_back.setProperty("cssClass", _translate("LogWindow", "btn-inverted")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + LogWindow = QtWidgets.QMainWindow() + ui = Ui_LogWindow() + ui.setupUi(LogWindow) + LogWindow.show() + sys.exit(app.exec_()) diff --git a/src/ui_elements/logwindow.ui b/src/ui_elements/logwindow.ui new file mode 100644 index 00000000..5e678acf --- /dev/null +++ b/src/ui_elements/logwindow.ui @@ -0,0 +1,121 @@ + + + LogWindow + + + + 0 + 0 + 800 + 480 + + + + + 0 + 0 + + + + + 800 + 480 + + + + + 800 + 480 + + + + ArrowCursor + + + Logs + + + + + + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + < Back + + + btn-inverted + + + + + + + + 105 + 38 + + + + + 5000 + 100 + + + + + 14 + 75 + true + + + + 11 + + + + + + + + + + 12 + + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAsNeeded + + + QTextEdit::NoWrap + + + + + + + + + diff --git a/src/ui_elements/optionwindow.py b/src/ui_elements/optionwindow.py index ede23acb..1053ebb2 100644 --- a/src/ui_elements/optionwindow.py +++ b/src/ui_elements/optionwindow.py @@ -28,60 +28,66 @@ def setupUi(self, Optionwindow): self.centralwidget.setObjectName("centralwidget") self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout.setObjectName("gridLayout") - self.button_back = QtWidgets.QPushButton(self.centralwidget) - self.button_back.setMaximumSize(QtCore.QSize(5000, 300)) + self.scrollArea = QtWidgets.QScrollArea(self.centralwidget) + self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame) + self.scrollArea.setFrameShadow(QtWidgets.QFrame.Plain) + self.scrollArea.setLineWidth(1) + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 782, 462)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents) + self.verticalLayout_3.setContentsMargins(0, 0, 4, 0) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.gridLayout_2 = QtWidgets.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.button_shutdown = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_shutdown.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_back.setFont(font) - self.button_back.setObjectName("button_back") - self.gridLayout.addWidget(self.button_back, 5, 0, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.button_reboot = QtWidgets.QPushButton(self.centralwidget) - self.button_reboot.setMaximumSize(QtCore.QSize(5000, 300)) + self.button_shutdown.setFont(font) + self.button_shutdown.setObjectName("button_shutdown") + self.gridLayout_2.addWidget(self.button_shutdown, 3, 1, 1, 1) + self.button_config = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_config.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_reboot.setFont(font) - self.button_reboot.setObjectName("button_reboot") - self.horizontalLayout.addWidget(self.button_reboot) - self.button_shutdown = QtWidgets.QPushButton(self.centralwidget) - self.button_shutdown.setMaximumSize(QtCore.QSize(5000, 300)) + self.button_config.setFont(font) + self.button_config.setObjectName("button_config") + self.gridLayout_2.addWidget(self.button_config, 1, 0, 1, 1) + self.button_export = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_export.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_shutdown.setFont(font) - self.button_shutdown.setObjectName("button_shutdown") - self.horizontalLayout.addWidget(self.button_shutdown) - self.gridLayout.addLayout(self.horizontalLayout, 4, 0, 1, 1) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.button_clean = QtWidgets.QPushButton(self.centralwidget) - self.button_clean.setMaximumSize(QtCore.QSize(5000, 300)) + self.button_export.setFont(font) + self.button_export.setObjectName("button_export") + self.gridLayout_2.addWidget(self.button_export, 1, 1, 1, 1) + self.button_calibration = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_calibration.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_clean.setFont(font) - self.button_clean.setObjectName("button_clean") - self.horizontalLayout_2.addWidget(self.button_clean) - self.button_calibration = QtWidgets.QPushButton(self.centralwidget) - self.button_calibration.setMaximumSize(QtCore.QSize(5000, 300)) + self.button_calibration.setFont(font) + self.button_calibration.setObjectName("button_calibration") + self.gridLayout_2.addWidget(self.button_calibration, 0, 1, 1, 1) + self.button_clean = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_clean.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_calibration.setFont(font) - self.button_calibration.setObjectName("button_calibration") - self.horizontalLayout_2.addWidget(self.button_calibration) - self.gridLayout.addLayout(self.horizontalLayout_2, 1, 0, 1, 1) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.button_backup = QtWidgets.QPushButton(self.centralwidget) + self.button_clean.setFont(font) + self.button_clean.setObjectName("button_clean") + self.gridLayout_2.addWidget(self.button_clean, 0, 0, 1, 1) + self.button_backup = QtWidgets.QPushButton(self.scrollAreaWidgetContents) self.button_backup.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) @@ -89,8 +95,17 @@ def setupUi(self, Optionwindow): font.setWeight(75) self.button_backup.setFont(font) self.button_backup.setObjectName("button_backup") - self.horizontalLayout_3.addWidget(self.button_backup) - self.button_restore = QtWidgets.QPushButton(self.centralwidget) + self.gridLayout_2.addWidget(self.button_backup, 2, 0, 1, 1) + self.button_reboot = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_reboot.setMaximumSize(QtCore.QSize(5000, 300)) + font = QtGui.QFont() + font.setPointSize(28) + font.setBold(True) + font.setWeight(75) + self.button_reboot.setFont(font) + self.button_reboot.setObjectName("button_reboot") + self.gridLayout_2.addWidget(self.button_reboot, 3, 0, 1, 1) + self.button_restore = QtWidgets.QPushButton(self.scrollAreaWidgetContents) self.button_restore.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) @@ -98,29 +113,38 @@ def setupUi(self, Optionwindow): font.setWeight(75) self.button_restore.setFont(font) self.button_restore.setObjectName("button_restore") - self.horizontalLayout_3.addWidget(self.button_restore) - self.gridLayout.addLayout(self.horizontalLayout_3, 3, 0, 1, 1) - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.button_config = QtWidgets.QPushButton(self.centralwidget) - self.button_config.setMaximumSize(QtCore.QSize(5000, 300)) + self.gridLayout_2.addWidget(self.button_restore, 2, 1, 1, 1) + self.button_back = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_back.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_config.setFont(font) - self.button_config.setObjectName("button_config") - self.horizontalLayout_4.addWidget(self.button_config) - self.button_export = QtWidgets.QPushButton(self.centralwidget) - self.button_export.setMaximumSize(QtCore.QSize(5000, 300)) + self.button_back.setFont(font) + self.button_back.setObjectName("button_back") + self.gridLayout_2.addWidget(self.button_back, 5, 0, 1, 2) + self.button_logs = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_logs.setMaximumSize(QtCore.QSize(5000, 300)) font = QtGui.QFont() font.setPointSize(28) font.setBold(True) font.setWeight(75) - self.button_export.setFont(font) - self.button_export.setObjectName("button_export") - self.horizontalLayout_4.addWidget(self.button_export) - self.gridLayout.addLayout(self.horizontalLayout_4, 2, 0, 1, 1) + self.button_logs.setFont(font) + self.button_logs.setObjectName("button_logs") + self.gridLayout_2.addWidget(self.button_logs, 4, 0, 1, 1) + self.button_rfid = QtWidgets.QPushButton(self.scrollAreaWidgetContents) + self.button_rfid.setEnabled(False) + self.button_rfid.setMaximumSize(QtCore.QSize(5000, 300)) + font = QtGui.QFont() + font.setPointSize(28) + font.setBold(True) + font.setWeight(75) + self.button_rfid.setFont(font) + self.button_rfid.setObjectName("button_rfid") + self.gridLayout_2.addWidget(self.button_rfid, 4, 1, 1, 1) + self.verticalLayout_3.addLayout(self.gridLayout_2) + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.gridLayout.addWidget(self.scrollArea, 4, 0, 1, 1) Optionwindow.setCentralWidget(self.centralwidget) self.retranslateUi(Optionwindow) @@ -129,15 +153,17 @@ def setupUi(self, Optionwindow): def retranslateUi(self, Optionwindow): _translate = QtCore.QCoreApplication.translate Optionwindow.setWindowTitle(_translate("Optionwindow", "Options")) - self.button_back.setText(_translate("Optionwindow", "< Back")) - self.button_reboot.setText(_translate("Optionwindow", "Reboot")) self.button_shutdown.setText(_translate("Optionwindow", "Shutdown")) - self.button_clean.setText(_translate("Optionwindow", "Cleaning")) + self.button_config.setText(_translate("Optionwindow", "Change Config")) + self.button_export.setText(_translate("Optionwindow", "Export")) self.button_calibration.setText(_translate("Optionwindow", "Calibration")) + self.button_clean.setText(_translate("Optionwindow", "Cleaning")) self.button_backup.setText(_translate("Optionwindow", "Backup")) + self.button_reboot.setText(_translate("Optionwindow", "Reboot")) self.button_restore.setText(_translate("Optionwindow", "Restore")) - self.button_config.setText(_translate("Optionwindow", "Change Config")) - self.button_export.setText(_translate("Optionwindow", "Export")) + self.button_back.setText(_translate("Optionwindow", "< Back")) + self.button_logs.setText(_translate("Optionwindow", "Logs")) + self.button_rfid.setText(_translate("Optionwindow", "Write RFID")) if __name__ == "__main__": diff --git a/src/ui_elements/optionwindow.ui b/src/ui_elements/optionwindow.ui index 4f4b4ea0..d0026609 100644 --- a/src/ui_elements/optionwindow.ui +++ b/src/ui_elements/optionwindow.ui @@ -39,201 +39,272 @@ - - - - - 5000 - 300 - + + + + QFrame::NoFrame - - - 28 - 75 - true - + + QFrame::Plain - - < Back + + 1 - - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Reboot - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Shutdown - - - - - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Cleaning - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Calibration - - - - - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Backup - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Restore - - - - - - - - - - - - 5000 - 300 - - - - - 28 - 75 - true - - - - Change Config + + true + + + + + 0 + 0 + 782 + 462 + + + + + 0 - - - - - - - 5000 - 300 - + + 0 - - - 28 - 75 - true - + + 4 - - Export + + 0 - - - + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Shutdown + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Change Config + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Export + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Calibration + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Cleaning + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Backup + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Reboot + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Restore + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + < Back + + + + + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Logs + + + + + + + false + + + + 5000 + 300 + + + + + 28 + 75 + true + + + + Write RFID + + + + + + + +