diff --git a/.github/workflows/dev-ci.yml b/.github/workflows/dev-ci.yml index 8fa61f99..556119c2 100644 --- a/.github/workflows/dev-ci.yml +++ b/.github/workflows/dev-ci.yml @@ -1,13 +1,6 @@ -name: dev-test-code +name: CI-dev-pipeline on: - push: - branches: - - dev - paths-ignore: - - "scripts/**" - - "BlocksScreen/lib/ui/**" - - "extras/**" pull_request: branches: - dev @@ -22,7 +15,8 @@ jobs: fail-fast: false matrix: python-version: ["3.11.2"] - test-type: [ruff, pylint, pytest] + test-type: [ruff, pylint, pytest, docstrcov, security] + steps: - name: Checkout repo uses: actions/checkout@v4 @@ -31,12 +25,14 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - cache: "pip" + cache: pip + cache-dependency-path: scripts/requirements-dev.txt + - name: Install dependencies run: | echo "Installing dependencies" python -m pip install --upgrade pip - pip install scripts -r scripts/requirements-dev.txt + pip install -r scripts/requirements-dev.txt - name: Run Test ${{ matrix.test-type }} run: | @@ -48,21 +44,31 @@ jobs: echo "Ruff finished" fi if [ "${{ matrix.test-type }}" == "pylint" ]; then - echo "Running Code Test" - pylint -j$(nproc) --recursive=y --rcfile=.pylintrc.dev . > pylint-output.txt 2>&1 + echo "Running Pylint Code Test" + pylint -j$(nproc) --recursive=y BlocksScreen/ > pylint-output.txt 2>&1 echo "Pylint finished" fi if [ "${{ matrix.test-type }}" == "pytest" ]; then if [ -d "tests/" ] && [ "$(ls -A tests/)" ]; then echo "Running Python unit tests" - pytest tests/'*.py' --doctest-modules --junitxml=junit/test-results.xml --cov=com --conv-report=xml --cov-report=html > pytest-output.txt 2>&1 + pytest tests/ --doctest-modules --junitxml=junit/test-results.xml --cov=BlocksScreen/ --cov-report=xml --cov-report=html > pytest-output.txt 2>&1 else echo "No tests directory no need to proceed with tests" fi fi - - name: Upload ruff artifact + if [ "${{ matrix.test-type }}" == "docstrcov" ]; then + echo "Running docstring coverage test" + docstr-coverage BlocksScreen/ --exclude '.*/BlocksScreen/lib/ui/.*?$' --fail-under=80 --skip-magic --skip-init --skip-private --skip-property > docstr-cov-output.txt 2>&1 + fi + + if [ "${{matrix.test-type }}" == "security" ]; then + echo "Running bandit security test" + bandit -c pyproject.toml -r . -f json -o bandit-output.json 2>&1 + fi + + - name: Upload ruff artifact if: always() && matrix.test-type == 'ruff' uses: actions/upload-artifact@v4 with: @@ -75,15 +81,29 @@ jobs: with: name: pylint-results path: pylint-output.txt - + - name: Upload Pytest Artifacts - if: always() && matrix.test-type == 'pytest' && hashFiles('pytest-output.txt', 'junit/test-results.xml', 'coverage.xml') + if: always() && matrix.test-type == 'pytest' uses: actions/upload-artifact@v4 with: name: pytest-results path: | - pytest_output.txt + pytest-output.txt junit/test-results.xml coverage.xml htmlcov/ - \ No newline at end of file + continue-on-error: true + + - name: Upload docstr coverage report + if: always() && matrix.test-type == 'docstrcov' + uses: actions/upload-artifact@v4 + with: + name: docstr-coverage + path: docstr-cov-output.txt + + - name: Upload bandit security report + if: always() && matrix.test-type == 'security' + uses: actions/upload-artifact@v4 + with: + name: bandit-output + path: bandit-output.json diff --git a/.github/workflows/stage-ci.yml b/.github/workflows/stage-ci.yml new file mode 100644 index 00000000..6d8de6be --- /dev/null +++ b/.github/workflows/stage-ci.yml @@ -0,0 +1,23 @@ +name: stage-ci + +on: + branches: + - stage + paths-ignore: + - "scripts/**" + - "BlocksScreen/lib/ui/**" + - "extras/**" + workflow_run: + workflows: ["dev-test-code"] + types: + - completed + jobs: + ci-stage: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Run staging pipeline + run: echo "Running staging integration tests..." \ No newline at end of file diff --git a/BlocksScreen/BlocksScreen.py b/BlocksScreen/BlocksScreen.py index 83ec0a3b..a7a2098e 100644 --- a/BlocksScreen/BlocksScreen.py +++ b/BlocksScreen/BlocksScreen.py @@ -22,16 +22,15 @@ RESET = "\033[0m" -def setup_working_dir(): ... - - def setup_app_loggers(): - ql = logger.create_logger(name="logs/BlocksScreen.log", level=logging.DEBUG) + """Setup logger""" + _ = logger.create_logger(name="logs/BlocksScreen.log", level=logging.DEBUG) _logger = logging.getLogger(name="logs/BlocksScreen.log") _logger.info("============ BlocksScreen Initializing ============") def show_splash(window: typing.Optional[QtWidgets.QWidget] = None): + """Show splash screen on app initialization""" logo = QtGui.QPixmap("BlocksScreen/BlocksScreen/lib/ui/resources/logoblocks.png") splash = QtWidgets.QSplashScreen(pixmap=logo) splash.setGeometry(QtCore.QRect(0, 0, 400, 200)) diff --git a/BlocksScreen/configfile.py b/BlocksScreen/configfile.py index 53ad0579..981ac4b2 100644 --- a/BlocksScreen/configfile.py +++ b/BlocksScreen/configfile.py @@ -47,10 +47,14 @@ class Sentinel(enum.Enum): + """Sentinel value to signify missing condition, absence of value""" + MISSING = object class ConfigError(Exception): + """Exception raised when Configfile errors exist""" + def __init__(self, msg) -> None: super().__init__(msg) self.msg = msg @@ -59,24 +63,10 @@ def __init__(self, msg) -> None: class BlocksScreenConfig: config = configparser.ConfigParser( allow_no_value=True, - # interpolation=configparser.ExtendedInterpolation(), - # delimiters=(":"), - # inline_comment_prefixes=("#"), - # comment_prefixes=("#", "#~#"), - # empty_lines_in_values=True, ) - update_pending: bool = False - _instance = None - # def __new__( - # cls, *args, **kwargs - # ) -> BlocksScreenConfig: # Singleton pattern - # if not cls._instance: - # cls._instance = super(BlocksScreenConfig, cls).__new__(cls) - # return cls._instance - def __init__( self, configfile: typing.Union[str, pathlib.Path], section: str ) -> None: @@ -93,24 +83,41 @@ def __contains__(self, key): return key in self.config def sections(self) -> typing.List[str]: + """Returns list of all sections""" return self.config.sections() def get_section( self, section: str, fallback: typing.Optional[T] = None ) -> BlocksScreenConfig: + """Get configfile section""" if not self.config.has_section(section): - raise configparser.NoSectionError( - f"No section with name: {section}" - ) + raise configparser.NoSectionError(f"No section with name: {section}") return BlocksScreenConfig(self.configfile, section) def get_options(self) -> list: + """Get section options""" return self.config.options(self.section) def has_section(self, section: str) -> bool: + """Check if config file has a section + + Args: + section (str): section name + + Returns: + bool: true if section exists, false otherwise + """ return bool(self.config.has_section(section)) def has_option(self, option: str) -> bool: + """Check if section has a option + + Args: + option (str): option name + + Returns: + bool: true if section exists, false otherwise + """ return bool(self.config.has_option(self.section, option)) def get( @@ -119,10 +126,18 @@ def get( parser: type = str, default: typing.Union[Sentinel, str, T] = Sentinel.MISSING, ) -> typing.Union[Sentinel, str]: + """Get option value + + Args: + option (str): option name + parser (type, optional): bool, float, int. Defaults to str. + default (typing.Union[Sentinel, str, T], optional): Default value for specified option. Defaults to Sentinel.MISSING. + + Returns: + typing.Union[Sentinel, str]: Requested option. Defaults to the specified default value + """ return parser( - self.config.get( - section=self.section, option=option, fallback=default - ) + self.config.get(section=self.section, option=option, fallback=default) ) def getint( @@ -130,15 +145,31 @@ def getint( option: str, default: typing.Union[Sentinel, int] = Sentinel.MISSING, ) -> typing.Union[Sentinel, int]: - return self.config.getint( - section=self.section, option=option, fallback=default - ) + """Get option value + + Args: + option (str): option name + default (typing.Union[Sentinel, int], optional): Default value for specified option. Defaults to Sentinel.MISSING. + + Returns: + typing.Union[Sentinel, int]: Requested option. + """ + return self.config.getint(section=self.section, option=option, fallback=default) def getfloat( self, option: str, default: typing.Union[Sentinel, float] = Sentinel.MISSING, ) -> typing.Union[Sentinel, float]: + """Get the value for the specified option + + Args: + option (str): option name + default (typing.Union[Sentinel, float], optional): Default value for specified option. Defaults to Sentinel.MISSING. + + Returns: + typing.Union[Sentinel, float]: _description_ + """ return self.config.getfloat( section=self.section, option=option, fallback=default ) @@ -148,6 +179,15 @@ def getboolean( option: str, default: typing.Union[Sentinel, bool] = Sentinel.MISSING, ) -> typing.Union[Sentinel, bool]: + """Get option value + + Args: + option (str): option name + default (typing.Union[Sentinel, bool], optional): Default value for specified option. Defaults to Sentinel.MISSING. + + Returns: + typing.Union[Sentinel, bool]: _description_ + """ return self.config.getboolean( section=self.section, option=option, fallback=default ) @@ -156,9 +196,7 @@ def _find_section_index(self, section: str) -> int: try: return self.raw_config.index("[" + section + "]") except ValueError as e: - raise configparser.Error( - f'Section "{section}" does not exist: {e}' - ) + raise configparser.Error(f'Section "{section}" does not exist: {e}') def _find_section_limits(self, section: str) -> typing.Tuple: try: @@ -189,6 +227,14 @@ def _find_option_index( ) def add_section(self, section: str) -> None: + """Add a section to configuration file + + Args: + section (str): section name + + Raises: + configparser.DuplicateSectionError: Exception thrown when section is duplicated + """ try: with self.file_lock: sec_string = f"[{section}]" @@ -209,9 +255,7 @@ def add_section(self, section: str) -> None: except configparser.DuplicateSectionError as e: logging.error(f'Section "{section}" already exists. {e}') except configparser.Error as e: - logging.error( - f'Unable to add "{section}" section to configuration: {e}' - ) + logging.error(f'Unable to add "{section}" section to configuration: {e}') def add_option( self, @@ -219,6 +263,13 @@ def add_option( option: str, value: typing.Union[str, None] = None, ) -> None: + """Add option with a value to a section + + Args: + section (str): section name + option (str): option name + value (typing.Union[str, None], optional): value for the specified option. Defaults to None. + """ try: with self.file_lock: section_start, section_end = self._find_section_limits(section) @@ -239,13 +290,12 @@ def add_option( ) def save_configuration(self) -> None: + """Save teh configuration to file""" try: if not self.update_pending: return with self.file_lock: - self.configfile.write_text( - "\n".join(self.raw_config), encoding="utf-8" - ) + self.configfile.write_text("\n".join(self.raw_config), encoding="utf-8") sio = io.StringIO() sio.writelines(self.raw_config) self.config.write(sio) @@ -257,13 +307,8 @@ def save_configuration(self) -> None: finally: self.update_pending = False - def _do_save(self, data) -> bool: - try: - return True - except Exception as e: - return False - def load_config(self): + """Load configuration file""" try: self.raw_config.clear() self.config.clear() # Reset configparser @@ -330,6 +375,7 @@ def _parse_file(self) -> typing.Tuple[typing.List[str], typing.Dict]: def get_configparser() -> BlocksScreenConfig: + """Loads configuration from file and returns that configuration""" wanted_target = os.path.join(DEFAULT_CONFIGFILE_PATH, "BlocksScreen.cfg") fallback = os.path.join(WORKING_DIR, "BlocksScreen.cfg") configfile = ( @@ -337,13 +383,9 @@ def get_configparser() -> BlocksScreenConfig: if check_file_on_path(DEFAULT_CONFIGFILE_PATH, "BlocksScreen.cfg") else fallback ) - try: - config_object = BlocksScreenConfig( - configfile=configfile, section="server" - ) - config_object.load_config() - if not config_object.has_section("server"): - raise ConfigError("Section [server] is missing from configuration") - except ConfigError: + config_object = BlocksScreenConfig(configfile=configfile, section="server") + config_object.load_config() + if not config_object.has_section("server"): logging.error("Error loading configuration file for the application.") + raise ConfigError("Section [server] is missing from configuration") return BlocksScreenConfig(configfile=configfile, section="server") diff --git a/BlocksScreen/events.py b/BlocksScreen/events.py index 05c5d28f..20a870e4 100644 --- a/BlocksScreen/events.py +++ b/BlocksScreen/events.py @@ -1,3 +1,5 @@ +"""Collection of all custom events used by the application""" + import typing from PyQt6.QtCore import QEvent @@ -21,6 +23,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(WebSocketConnecting.WebsocketConnectingEvent) @@ -48,9 +51,8 @@ def __init__( @staticmethod def type() -> QEvent.Type: - return QEvent.Type( - WebSocketMessageReceived.WebsocketMessageReceivedEvent - ) + """Return event type""" + return QEvent.Type(WebSocketMessageReceived.WebsocketMessageReceivedEvent) class WebSocketOpen(QEvent): @@ -70,6 +72,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(WebSocketOpen.WebsocketOpenEvent) @@ -83,15 +86,14 @@ class WebSocketError(QEvent): WebsocketErrorEvent = QEvent.Type(QEvent.registerEventType()) def __init__(self, data, *args, **kwargs): - super(WebSocketError, self).__init__( - WebSocketError.WebsocketErrorEvent - ) + super(WebSocketError, self).__init__(WebSocketError.WebsocketErrorEvent) self.data = data self.args = args self.kwargs = kwargs @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(WebSocketError.WebsocketErrorEvent) @@ -114,6 +116,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(WebSocketDisconnected.WebsocketDisconnectedEvent) @@ -128,15 +131,14 @@ class WebSocketClose(QEvent): WebsocketCloseEvent = QEvent.Type(QEvent.registerEventType()) def __init__(self, data, *args, **kwargs): - super(WebSocketClose, self).__init__( - WebSocketClose.WebsocketCloseEvent - ) + super(WebSocketClose, self).__init__(WebSocketClose.WebsocketCloseEvent) self.data = data self.args = args self.kwargs = kwargs @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(WebSocketClose.WebsocketCloseEvent) @@ -151,15 +153,14 @@ class KlippyShutdown(QEvent): def __init__(self, data, *args, **kwargs): QEvent.__instancecheck__(self) - super(KlippyShutdown, self).__init__( - KlippyShutdown.KlippyShutdownEvent - ) + super(KlippyShutdown, self).__init__(KlippyShutdown.KlippyShutdownEvent) self.data = data self.args = args self.kwargs = kwargs @staticmethod def type() -> QEvent.Type: + """Return event type""" return KlippyShutdown.KlippyShutdownEvent # def __instancecheck__(self, instance: Any) -> bool: @@ -186,6 +187,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(KlippyReady.KlippyReadyEvent) @@ -208,6 +210,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(KlippyDisconnected.KlippyDisconnectedEvent) @@ -227,6 +230,7 @@ def __init__(self, data, message, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(KlippyError.KlippyErrorEvent) @@ -244,9 +248,7 @@ class ReceivedFileData(QEvent): def __init__( self, data, method, params, /, *args, **kwargs ): # Positional-only arguments "data", "method", "params", these need to be inserted in order or it wont work - super(ReceivedFileData, self).__init__( - ReceivedFileData.ReceivedFileDataEvent - ) + super(ReceivedFileData, self).__init__(ReceivedFileData.ReceivedFileDataEvent) self.data = data self.method = method self.params = params @@ -255,6 +257,7 @@ def __init__( @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(ReceivedFileData.ReceivedFileDataEvent) @@ -276,6 +279,7 @@ def __init__(self, filename, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(PrintStart.PrintStartEvent) @@ -296,6 +300,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(PrintComplete.PrintCompleteEvent) @@ -317,6 +322,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(PrintPause.PrintPauseEvent) @@ -338,6 +344,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(PrintResume.PrintResumeEvent) @@ -351,9 +358,7 @@ class PrintCancelled(QEvent): PrintCancelledEvent = QEvent.Type(QEvent.registerEventType()) def __init__(self, data, *args, **kwargs): - super(PrintCancelled, self).__init__( - PrintCancelled.PrintCancelledEvent - ) + super(PrintCancelled, self).__init__(PrintCancelled.PrintCancelledEvent) self.data = data self.args = args @@ -361,6 +366,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(PrintCancelled.PrintCancelledEvent) @@ -381,6 +387,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(PrintError.PrintErrorEvent) @@ -401,6 +408,7 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(NetworkAdded.NetworkAddedEvent) @@ -414,15 +422,14 @@ class NetworkDeleted(QEvent): NetworkDeletedEvent = QEvent.Type(QEvent.registerEventType()) def __init__(self, data, *args, **kwargs): - super(NetworkDeleted, self).__init__( - NetworkDeleted.NetworkDeletedEvent - ) + super(NetworkDeleted, self).__init__(NetworkDeleted.NetworkDeletedEvent) self.data = data self.args = args self.kwargs = kwargs @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(NetworkDeleted) @@ -443,4 +450,5 @@ def __init__(self, data, *args, **kwargs): @staticmethod def type() -> QEvent.Type: + """Return event type""" return QEvent.Type(NetworkScan.NetworkScanEvent) diff --git a/BlocksScreen/helper_methods.py b/BlocksScreen/helper_methods.py index b086c751..0dd2b2db 100644 --- a/BlocksScreen/helper_methods.py +++ b/BlocksScreen/helper_methods.py @@ -1,7 +1,9 @@ +# Collection of useful methods +# # This file contains some methods derived from KlipperScreen # Original source: https://github.com/KlipperScreen/KlipperScreen # License: GNU General Public License v3 -# Modifications made by Hugo Costa (2025) for BlocksScreen +# Modifications made by Hugo Costa (2025) for BlocksScreen import ctypes @@ -18,6 +20,8 @@ libxext = ctypes.CDLL("libXext.so.6") class DPMSState(enum.Enum): + """Available DPMS states""" + FAIL = -1 ON = 0 STANDBY = 1 @@ -35,6 +39,7 @@ class DPMSState(enum.Enum): libxext.DPMSForceLevel.restype = ctypes.c_int def get_dpms_state(): + """Gets and returns DPMS state""" _dpms_state = DPMSState.FAIL _display_name = ctypes.c_char_p(b":0") libxext.XOpenDisplay.restype = ctypes.c_void_p @@ -59,6 +64,11 @@ def get_dpms_state(): return _dpms_state def set_dpms_mode(mode: DPMSState) -> None: + """Set DPMS state + + Args: + mode (DPMSState): State to set DPMS. Check available state on `DPMSState` + """ _display_name = ctypes.c_char_p(b":0") libxext.XOpenDisplay.restype = ctypes.c_void_p display = ctypes.c_void_p( @@ -76,6 +86,7 @@ def set_dpms_mode(mode: DPMSState) -> None: libxext.XCloseDisplay(display) def get_dpms_timeouts() -> typing.Dict: + """Get current DPMS timeouts""" _display_name = ctypes.c_char_p(b":0") libxext.XOpenDisplay.restype = ctypes.c_void_p display = ctypes.c_void_p( @@ -93,9 +104,7 @@ def get_dpms_timeouts() -> typing.Dict: suspend_p = ctypes.create_string_buffer(2) off_p = ctypes.create_string_buffer(2) - if libxext.DPMSGetTimeouts( - display, standby_p, suspend_p, off_p - ): + if libxext.DPMSGetTimeouts(display, standby_p, suspend_p, off_p): _standby_timeout = struct.unpack("H", standby_p.raw)[0] _suspend_timeout = struct.unpack("H", suspend_p.raw)[0] _off_timeout = struct.unpack("H", off_p.raw)[0] @@ -111,6 +120,7 @@ def get_dpms_timeouts() -> typing.Dict: def set_dpms_timeouts( suspend: int = 0, standby: int = 0, off: int = 0 ) -> typing.Dict: + """Set DPMS timeout""" _display_name = ctypes.c_char_p(b":0") libxext.XOpenDisplay.restype = ctypes.c_void_p display = ctypes.c_void_p( @@ -131,9 +141,7 @@ def set_dpms_timeouts( suspend_p = ctypes.create_string_buffer(2) off_p = ctypes.create_string_buffer(2) - if libxext.DPMSGetTimeouts( - display, standby_p, suspend_p, off_p - ): + if libxext.DPMSGetTimeouts(display, standby_p, suspend_p, off_p): _standby_timeout = struct.unpack("H", standby_p.raw)[0] _suspend_timeout = struct.unpack("H", suspend_p.raw)[0] _off_timeout = struct.unpack("H", off_p.raw)[0] @@ -147,6 +155,11 @@ def set_dpms_timeouts( } def get_dpms_info() -> typing.Dict: + """Get DPMS information + + Returns: + typing.Dict: Dpms state + """ _dpms_state = DPMSState.FAIL onoff = 0 _display_name = ctypes.c_char_p(b":0") @@ -176,6 +189,12 @@ def get_dpms_info() -> typing.Dict: return {"power_level": onoff, "state": DPMSState(_dpms_state)} def check_dpms_capable(display: int): + """Check if device has DPMS + + Args: + display (int): Display index + + """ _display_name = ctypes.c_char_p(b":%d" % (display)) libxext.XOpenDisplay.restype = ctypes.c_void_p @@ -198,6 +217,7 @@ def check_dpms_capable(display: int): return _capable def disable_dpms() -> None: + """Disable DPMS""" set_dpms_mode(DPMSState.OFF) except OSError as e: @@ -255,6 +275,7 @@ def estimate_print_time(seconds: int) -> list: def normalize(value, r_min=0.0, r_max=1.0, t_min=0.0, t_max=100): + """Normalize values between a rage""" # https://stats.stackexchange.com/questions/281162/scale-a-number-between-a-range c1 = (value - r_min) / (r_max - r_min) c2 = (t_max - t_min) + t_min @@ -290,6 +311,7 @@ def check_filepath_permission(filepath, access_type: int = os.R_OK) -> bool: def check_dir_existence( directory: typing.Union[str, pathlib.Path], ) -> bool: + """Check if a directory exists. Returns a true if it exists""" if isinstance(directory, pathlib.Path): return bool(directory.is_dir()) return bool(os.path.isdir(directory)) @@ -299,20 +321,6 @@ def check_file_on_path( path: typing.Union[typing.LiteralString, pathlib.Path], filename: typing.Union[typing.LiteralString, pathlib.Path], ) -> bool: + """Check if file exists on path. Returns true if file exists on that specified directory""" _filepath = os.path.join(path, filename) return os.path.exists(_filepath) - - -def get_file_loc(filename) -> pathlib.Path: - ... - - -# def get_hash(data) -> hashlib._Hash: -# hash = hashlib.sha256() -# hash.update(data.encode()) -# hash.digest() -# return hash - - - -def digest_hash() -> None: ... diff --git a/BlocksScreen/lib/async_network_monitor.py b/BlocksScreen/lib/async_network_monitor.py deleted file mode 100644 index 6063be30..00000000 --- a/BlocksScreen/lib/async_network_monitor.py +++ /dev/null @@ -1,157 +0,0 @@ -import asyncio -import logging -import threading -import typing - -import sdbus -from PyQt6 import QtCore -from sdbus_async import networkmanager - - -class SdbusNMMonitor(QtCore.QObject): - state_change: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( - str, name="nm-state-changed" - ) - prop_changed: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( - name="nm-properties-changed" - ) - added_conn: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( - str, name="nm-conn-added" - ) - rem_con: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( - str, name="nm-conn-added" - ) - - def __init__(self) -> None: - super().__init__() - - self._running: bool = False # control - # Run on separate thread - self.thread: threading.Thread = threading.Thread( - name="asyncio.NMonitor.run_forever", - target=self._run_loop, - ) - self.thread.daemon = False # Do not exit the thread - - # Create a new asyncio loop - self.loop = asyncio.new_event_loop() - - # Asyncio Event - self.stop_event = asyncio.Event() - self.stop_event.clear() - # open asd set system sdbus - self.system_dbus = sdbus.sd_bus_open_system() - if not self.system_dbus: - logging.info("No dbus found, async network monitor exiting") - del self - return - sdbus.set_default_bus(self.system_dbus) - - # Instantiate NetworkManager - self.nm = networkmanager.NetworkManager() - - # Start thread - self.thread.start() - - if self.thread.is_alive(): - logging.info( - f"Sdbus NetworkManager Monitor Thread {self.thread.name} Running" - ) - - def close(self) -> None: - self._running = False - if hasattr(self, "state_listener_task"): - self.state_listener_task.cancel() - if hasattr(self, "added_ap_listener_task"): - self.added_ap_listener_task.cancel() - if hasattr(self, "rem_ap_listener_task"): - self.rem_ap_listener_task.cancel() - if hasattr(self, "prop_changed_listener_task"): - self.prop_changed_listener_task.cancel() - try: - self.loop.run_until_complete(self.state_listener_task) - self.loop.run_until_complete(self.added_ap_listener_task) - self.loop.run_until_complete(self.rem_ap_listener_task) - except asyncio.CancelledError as e: - logging.error(f"Exception while cancelling {e}") - self.stop_event.set() - self.loop.call_soon_threadsafe(self.stop_event.set) - self.loop.close() - self.thread.join() - - def _run_loop(self) -> None: - try: - asyncio.set_event_loop(self.loop) - self.loop.run_until_complete(asyncio.gather(self.monitor())) - except Exception as e: - logging.error(f"Exception on loop coroutine: {e}") - - async def monitor(self) -> None: - try: - self._running = True - self.state_listener_task = self.loop.create_task( - self._state_change_listener() - ) - self.added_ap_listener_task = self.loop.create_task( - self._access_added_listener() - ) - self.rem_ap_listener_task = self.loop.create_task( - self._access_rem_listener() - ) - self.prop_changed_listener_task = self.loop.create_task( - self._properties_changed_listener() - ) - await ( - self.stop_event.wait() - ) # Wait until .set() is done on self.stop_event - except Exception as e: - logging.error(f"Exception on monitor coroutine: {e}") - - async def _state_change_listener(self) -> None: - while self._running: - try: - logging.debug( - "Listening coroutine for NetworkManager state signal..." - ) - async for state in self.nm.state_changed: - enum_state = networkmanager.NetworkManagerState(state) - logging.debug( - f"NM State Changed: {enum_state.name} ({state})" - ) - self.state_change.emit(state) - except Exception as e: - logging.error(f"Exception on NM state listener: {e}") - - async def _properties_changed_listener(self) -> None: - while self._running: - try: - logging.debug( - "Listening coroutine for NetworkManager state signal..." - ) - async for state in self.nm.properties_changed: - enum_state = networkmanager.NetworkManagerState(state) - logging.debug( - f"NM State Changed: {enum_state.name} ({state})" - ) - self.state_change.emit(state) - except Exception as e: - logging.error(f"Exception on NM state listener: {e}") - - async def _access_added_listener(self) -> None: - while self._running: - try: - logging.debug("Listening coroutine for added access points") - async for ac in self.nm.device_added: - logging.debug(f"Signal for device added received {ac}") - self.added_conn.emit(ac) - except Exception as e: - logging.error(f"Error for added access points listener: {e}") - - async def _access_rem_listener(self) -> None: - while self._running: - try: - logging.debug("Listening coroutine for removed access points") - async for ac in self.nm.device_removed: - self.rem_con.emit(ac) - except Exception as e: - logging.error(f"Error for removed access points listener: {e}") diff --git a/BlocksScreen/lib/filament.py b/BlocksScreen/lib/filament.py index cafb053e..cb4c0232 100644 --- a/BlocksScreen/lib/filament.py +++ b/BlocksScreen/lib/filament.py @@ -1,20 +1,23 @@ -from typing import Optional - +# Class that represents a filament spool +from typing import Optional import enum -# typing.Optional[type()] == typing.Union[type(), None] - - class Filament: + """Filament spool""" + class SpoolBaseWeights(enum.Enum): # XXX This enum will probably be unnecessary + """Spool base weights""" + MINI = 750 BASE = 1000 BIG = 3000 JUMBO = 5000 class SpoolMaterial(enum.Flag): + """Spool material types""" + PLASTIC = enum.auto() PAPER = enum.auto() UNKNOWN = -1 @@ -80,10 +83,4 @@ def spool_type(self, new): raise ValueError( "Spool Material type is invalid" ) # Correct type but invalid option - else: - raise TypeError("") # TODO: Finish this type raise self._spool_type = new - - def calc_remaining_weight(self): ... # TODO calculate remaining spool weight - - def calc_initial_weight(self): ... # TODO calculate initial spool weight diff --git a/BlocksScreen/lib/files.py b/BlocksScreen/lib/files.py index 02849191..68a399c2 100644 --- a/BlocksScreen/lib/files.py +++ b/BlocksScreen/lib/files.py @@ -1,3 +1,6 @@ +# +# Gcode File manager +# from __future__ import annotations import os @@ -15,9 +18,7 @@ class Files(QtCore.QObject): [], [str], [str, bool], name="api-get-dir-info" ) request_file_metadata = QtCore.pyqtSignal([str], name="get_file_metadata") - request_files_thumbnails = QtCore.pyqtSignal( - [str], name="request_files_thumbnail" - ) + request_files_thumbnails = QtCore.pyqtSignal([str], name="request_files_thumbnail") request_file_download = QtCore.pyqtSignal([str, str], name="file_download") on_dirs: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( list, name="on-dirs" @@ -44,16 +45,10 @@ def __init__( self.request_file_list.connect(slot=self.ws.api.get_file_list) self.request_file_list[str].connect(slot=self.ws.api.get_file_list) self.request_dir_info.connect(slot=self.ws.api.get_dir_information) - self.request_dir_info[str, bool].connect( - self.ws.api.get_dir_information - ) - self.request_dir_info[str].connect( - slot=self.ws.api.get_dir_information - ) + self.request_dir_info[str, bool].connect(self.ws.api.get_dir_information) + self.request_dir_info[str].connect(slot=self.ws.api.get_dir_information) self.request_file_metadata.connect(slot=self.ws.api.get_gcode_metadata) - self.request_files_thumbnails.connect( - slot=self.ws.api.get_gcode_thumbnail - ) + self.request_files_thumbnails.connect(slot=self.ws.api.get_gcode_thumbnail) self.request_file_download.connect(slot=self.ws.api.download_file) QtWidgets.QApplication.instance().installEventFilter(self) # type: ignore @@ -62,15 +57,13 @@ def file_list(self): return self.files def handle_message_received(self, method: str, data, params: dict) -> None: + """Handle file related messages received by moonraker""" if "server.files.list" in method: # Get all files in root and its subdirectories and # request their metadata self.files.clear() self.files = data - [ - self.request_file_metadata.emit(item["path"]) - for item in self.files - ] + [self.request_file_metadata.emit(item["path"]) for item in self.files] elif "server.files.metadata" in method: if data["filename"] in self.files_metadata.keys(): if not data.get("filename", None): @@ -89,8 +82,11 @@ def handle_message_received(self, method: str, data, params: dict) -> None: @QtCore.pyqtSlot(str, name="on_request_fileinfo") def on_request_fileinfo(self, filename: str) -> None: - # if not filename: - # return + """Requests metadata for a file + + Args: + filename (str): file + """ _data: dict = { "thumbnail_images": list, "filament_total": dict, @@ -122,34 +118,18 @@ def on_request_fileinfo(self, filename: str) -> None: _thumbnails, ) ) - _thumbnail_images = list( - map(lambda path: QtGui.QImage(path), _thumbnail_paths) - ) + _thumbnail_images = list(map(lambda path: QtGui.QImage(path), _thumbnail_paths)) _data.update({"thumbnail_images": _thumbnail_images}) - _data.update( - {"filament_total": _file_metadata.get("filament_total", "?")} - ) - _data.update( - {"estimated_time": _file_metadata.get("estimated_time", 0)} - ) + _data.update({"filament_total": _file_metadata.get("filament_total", "?")}) + _data.update({"estimated_time": _file_metadata.get("estimated_time", 0)}) _data.update({"layer_count": _file_metadata.get("layer_count", -1.0)}) _data.update({"total_layer": _file_metadata.get("total_layer", -1.0)}) + _data.update({"object_height": _file_metadata.get("object_height", -1.0)}) + _data.update({"nozzle_diameter": _file_metadata.get("nozzle_diameter", -1.0)}) + _data.update({"layer_height": _file_metadata.get("layer_height", -1.0)}) _data.update( - {"object_height": _file_metadata.get("object_height", -1.0)} - ) - _data.update( - {"nozzle_diameter": _file_metadata.get("nozzle_diameter", -1.0)} - ) - _data.update( - {"layer_height": _file_metadata.get("layer_height", -1.0)} - ) - _data.update( - { - "first_layer_height": _file_metadata.get( - "first_layer_height", -1.0 - ) - } + {"first_layer_height": _file_metadata.get("first_layer_height", -1.0)} ) _data.update( { @@ -159,37 +139,24 @@ def on_request_fileinfo(self, filename: str) -> None: } ) _data.update( - { - "first_layer_bed_temp": _file_metadata.get( - "first_layer_bed_temp", -1.0 - ) - } - ) - _data.update( - {"chamber_temp": _file_metadata.get("chamber_temp", -1.0)} + {"first_layer_bed_temp": _file_metadata.get("first_layer_bed_temp", -1.0)} ) + _data.update({"chamber_temp": _file_metadata.get("chamber_temp", -1.0)}) + _data.update({"filament_name": _file_metadata.get("filament_name", -1.0)}) + _data.update({"filament_type": _file_metadata.get("filament_type", -1.0)}) _data.update( - {"filament_name": _file_metadata.get("filament_name", -1.0)} - ) - _data.update( - {"filament_type": _file_metadata.get("filament_type", -1.0)} - ) - _data.update( - { - "filament_weight_total": _file_metadata.get( - "filament_weight_total", -1.0 - ) - } + {"filament_weight_total": _file_metadata.get("filament_weight_total", -1.0)} ) _data.update({"slicer": _file_metadata.get("slicer", -1.0)}) self.fileinfo.emit(_data) def eventFilter(self, a0: QtCore.QObject, a1: QtCore.QEvent) -> bool: + """Filter Klippy related events""" if a1.type() == events.KlippyDisconnected.type(): self.files_metadata.clear() self.files.clear() return False - elif a1.type() == events.KlippyReady.type(): + if a1.type() == events.KlippyReady.type(): # Request all files including in subdirectories # in order to get all metadata self.request_file_list.emit() @@ -199,6 +166,7 @@ def eventFilter(self, a0: QtCore.QObject, a1: QtCore.QEvent) -> bool: return super().eventFilter(a0, a1) def event(self, a0: QtCore.QEvent) -> bool: + """Filter ReceivedFileData event""" if a0.type() == ReceivedFileData.type(): if isinstance(a0, ReceivedFileData): self.handle_message_received(a0.method, a0.data, a0.params) diff --git a/BlocksScreen/lib/machine.py b/BlocksScreen/lib/machine.py index 6258a94b..e1c4a0ca 100644 --- a/BlocksScreen/lib/machine.py +++ b/BlocksScreen/lib/machine.py @@ -1,45 +1,55 @@ +# +# Machine manager +# import logging -import subprocess +import shlex +import subprocess # nosec: B404 import typing -from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot +from PyQt6 import QtCore -class MachineControl(QObject): - service_restart = pyqtSignal(str, name="service-restart") +class MachineControl(QtCore.QObject): + service_restart = QtCore.pyqtSignal(str, name="service-restart") - def __init__(self, parent: typing.Optional["QObject"]) -> None: + def __init__(self, parent: typing.Optional["QtCore.QObject"]) -> None: super(MachineControl, self).__init__(parent) self.setObjectName("MachineControl") - - @pyqtSlot(name="machine_restart") + + @QtCore.pyqtSlot(name="machine_restart") def machine_restart(self): + """Reboot machine""" return self._run_command("sudo reboot now") - @pyqtSlot(name="machine_shutdown") + @QtCore.pyqtSlot(name="machine_shutdown") def machine_shutdown(self): + """Shutdown machine""" return self._run_command("sudo shutdown now") - @pyqtSlot(name="restart_klipper_service") + @QtCore.pyqtSlot(name="restart_klipper_service") def restart_klipper_service(self): - # self.service_restart.emit("restart-klipper-service") + """Restart klipper service""" return self._run_command("sudo systemctl stop klipper.service") - - @pyqtSlot(name="restart_moonraker_service") + + @QtCore.pyqtSlot(name="restart_moonraker_service") def restart_moonraker_service(self): - # self.service_restart.emit("restart-moonraker-service") + """Restart moonraker service""" return self._run_command("sudo systemctl restart moonraker.service") - def restart_bo_service(self): - # TODO: Restart Blocks Screen service, implement it later on - pass - def check_service_state(self, service_name: str): + """Check service status + + Args: + service_name (str): service name + + Returns: + _type_: output of the command `systemctl is-active ` + """ if service_name is None: return None return self._run_command(f"systemctl is-active {service_name}") - - def _run_command(self, command): + + def _run_command(self, command: str): """Runs a shell command. Args: @@ -50,18 +60,26 @@ def _run_command(self, command): """ try: - # REVIEW: Safe way to run bash commands - # * Old way, it didn't let me use grep commands or use | on the command - # cmd = shlex.split(command,posix=False) - # exec = cmd[0] - # exec_options = cmd[1:] - # output = subprocess.run( - # ([exec] + exec_options), capture_output=True) - # TEST: is this safe to use like this, or is it susceptible to attacks and stuff - p = subprocess.Popen( - command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + # Split command into a list of strings + cmd = shlex.split(command) + p = subprocess.run( # nosec: B603 + cmd, check=True, capture_output=True, text=True, timeout=5 + ) + return p.stdout.strip() + "\n" + p.stderr.strip() + except ValueError as e: + logging.error("Failed to parse command string '%s': '%s'", command, e) + raise RuntimeError(f"Invalid command format: {e}") from e + except subprocess.CalledProcessError as e: + logging.error( + "Caught exception (exit code %d) failed to run command: %s \nStderr: %s", + e.returncode, + command, + e.stderr.strip(), ) - output, e = p.communicate() - return output - except subprocess.SubprocessError: - logging.error("Error running commas : %s", command) + raise + except ( + subprocess.SubprocessError, + subprocess.TimeoutExpired, + FileNotFoundError, + ): + logging.error("Caught exception failed to run command %s", command) diff --git a/BlocksScreen/lib/moonrakerComm.py b/BlocksScreen/lib/moonrakerComm.py index 3519cf01..b2f41446 100644 --- a/BlocksScreen/lib/moonrakerComm.py +++ b/BlocksScreen/lib/moonrakerComm.py @@ -1,11 +1,10 @@ +# Moonraker api import json import logging import threading import websocket from events import ( - KlippyDisconnected, - KlippyShutdown, WebSocketDisconnected, WebSocketError, WebSocketMessageReceived, @@ -17,11 +16,6 @@ _logger = logging.getLogger(name="logs/BlocksScreen.log") -RED = "\033[31m" -GREEN = "\033[32m" -YELLOW = "\033[33m" -RESET = "\033[0m" - class OneShotTokenError(Exception): """Raised when unable to get oneshot token to connect to a websocket""" @@ -77,17 +71,20 @@ def __init__(self, parent: QtCore.QObject) -> None: @QtCore.pyqtSlot(name="retry_wb_conn") def retry_wb_conn(self): + """Retry websocket connection""" if self.connecting is True and self.connected is False: return False self._reconnect_count = 0 self.try_connection() def try_connection(self): + """Try connecting to websocket""" self.connecting = True self._retry_timer = RepeatedTimer(self.timeout, self.reconnect) return self.connect() def reconnect(self): + """Reconnect to websocket""" if self.connected: return True @@ -115,6 +112,7 @@ def reconnect(self): return self.connect() def connect(self) -> bool: + """Connect to websocket""" if self.connected: _logger.info("Connection established") return True @@ -320,7 +318,6 @@ def send_request(self, method: str, params: dict = {}) -> bool: return False self._request_id += 1 - # REVIEW: This data structure could be better, think about other implementations self.request_table[self._request_id] = [method, params] packet = { "jsonrpc": "2.0", @@ -331,18 +328,6 @@ def send_request(self, method: str, params: dict = {}) -> bool: self.ws.send(json.dumps(packet)) return True - # def customEvent(self, event: QtCore.QEvent | None) -> None: - # if not event: - # return - - # if ( - # event.type() == KlippyDisconnected.type() - # or event.type() == KlippyShutdown.type() - # ): - # # * Received notify_klippy_disconnected, start querying server information again to check if klipper is available - # self.evaluate_klippy_status() - # return super().customEvent(event) - class MoonAPI(QtCore.QObject): def __init__(self, ws: MoonWebSocket): @@ -351,12 +336,13 @@ def __init__(self, ws: MoonWebSocket): @QtCore.pyqtSlot(name="api_query_server_info") def api_query_server_info(self): - _logger.debug("Requested server.info") + """Query server information""" return self._ws.send_request(method="server.info") def identify_connection( self, client_name, version, type, url, access_token, api_key ): + """Request moonraker to identify connection""" return self._ws.send_request( method="server.connection.identify", params={ @@ -370,6 +356,7 @@ def identify_connection( ) def request_temperature_cached_data(self, include_monitors: bool = False): + """Request stored temperature monitors""" return self._ws.send_request( method="server.temperature_store", params={"include_monitors": include_monitors}, @@ -377,30 +364,36 @@ def request_temperature_cached_data(self, include_monitors: bool = False): @QtCore.pyqtSlot(name="query_printer_info") def request_printer_info(self): + """Requested printer information""" return self._ws.send_request(method="printer.info") @QtCore.pyqtSlot(name="get_available_objects") def get_available_objects(self): + """Request available printer objects""" return self._ws.send_request(method="printer.objects.list") @QtCore.pyqtSlot(dict, name="query_object") def object_query(self, objects: dict): + """Query printer object""" return self._ws.send_request( method="printer.objects.query", params={"objects": objects} ) @QtCore.pyqtSlot(dict, name="object_subscription") def object_subscription(self, objects: dict): + """Subscribe to printer object""" return self._ws.send_request( method="printer.objects.subscribe", params={"objects": objects} ) @QtCore.pyqtSlot(name="ws_query_endstops") def query_endstops(self): + """Query printer endstops""" return self._ws.send_request(method="printer.query_endstops.status") @QtCore.pyqtSlot(str, name="run_gcode") def run_gcode(self, gcode: str): + """Run Gcode""" if isinstance(gcode, str) is False or gcode is None: return False return self._ws.send_request( @@ -408,36 +401,45 @@ def run_gcode(self, gcode: str): ) def gcode_help(self): + """Request Gcode information""" return self._ws.send_request(method="printer.gcode.help") @QtCore.pyqtSlot(str, name="start_print") def start_print(self, filename): + """Start print job""" return self._ws.send_request( method="printer.print.start", params={"filename": filename} ) @QtCore.pyqtSlot(name="pause_print") def pause_print(self): + """Pause print job""" return self._ws.send_request(method="printer.print.pause") @QtCore.pyqtSlot(name="resume_print") def resume_print(self): + """Resume print job""" return self._ws.send_request(method="printer.print.resume") @QtCore.pyqtSlot(name="stop_print") def cancel_print(self): + """Cancel print job""" return self._ws.send_request(method="printer.print.cancel") - def machine_system(self): + def machine_shutdown(self): + """Request machine shutdown""" return self._ws.send_request(method="machine.shutdown") def machine_reboot(self): + """Request machine reboot""" return self._ws.send_request(method="machine.reboot") def restart_server(self): + """Request server restart""" return self._ws.send_request(method="server.restart") def restart_service(self, service): + """Request service restart""" if service is None or isinstance(service, str) is False: return False return self._ws.send_request( @@ -446,21 +448,16 @@ def restart_service(self, service): @QtCore.pyqtSlot(name="firmware_restart") def firmware_restart(self): - """firmware_restart + """Request Klipper firmware restart HTTP_REQUEST: POST /printer/firmware_restart JSON_RPC_REQUEST: printer.firmware_restart - Returns: - _type_: _description_ """ - # REVIEW: Whether i should send a websocket request or a post with http - # return self._ws._moonRest.firmware_restart() # With HTTP - return self._ws.send_request( - method="printer.firmware_restart" - ) # With Websocket + return self._ws.send_request(method="printer.firmware_restart") def stop_service(self, service): + """Request service stop""" if service is None or isinstance(service, str) is False: return False return self._ws.send_request( @@ -468,6 +465,7 @@ def stop_service(self, service): ) def start_service(self, service): + """Request service start""" if service is None or isinstance(service, str) is False: return False return self._ws.send_request( @@ -475,6 +473,7 @@ def start_service(self, service): ) def get_sudo_info(self, permission: bool = False): + """Request sudo privileges information""" if isinstance(permission, bool) is False: return False return self._ws.send_request( @@ -482,15 +481,19 @@ def get_sudo_info(self, permission: bool = False): ) def get_usb_devices(self): + """Request available usb devices""" return self._ws.send_request(method="machine.peripherals.usb") def get_serial_devices(self): + """Request available serial devices""" return self._ws.send_request(method="machine.peripherals.serial") def get_video_devices(self): + """Request available video devices""" return self._ws.send_request(method="machine.peripherals.video") def get_cabus_devices(self, interface: str = "can0"): + """Request available CAN devices""" return self._ws.send_request( method="machine.peripherals.canbus", params={"interface": interface}, @@ -499,16 +502,19 @@ def get_cabus_devices(self, interface: str = "can0"): @QtCore.pyqtSlot(name="api-request-file-list") @QtCore.pyqtSlot(str, name="api-request-file-list") def get_file_list(self, root_folder: str = "gcodes"): + """Get available files""" return self._ws.send_request( method="server.files.list", params={"root": root_folder} ) @QtCore.pyqtSlot(name="api-list-roots") def list_registered_roots(self): + """Get available root directories""" return self._ws.send_request(method="server.files.roots") @QtCore.pyqtSlot(str, name="api_request_file_list") def get_gcode_metadata(self, filename_dir: str): + """Request gcode metadata""" if not isinstance(filename_dir, str) or not filename_dir: return False return self._ws.send_request( @@ -517,6 +523,7 @@ def get_gcode_metadata(self, filename_dir: str): @QtCore.pyqtSlot(str, name="api-scan-gcode-metadata") def scan_gcode_metadata(self, filename_dir: str): + """Scan gcode metadata""" if isinstance(filename_dir, str) is False or filename_dir is None: return False return self._ws.send_request( @@ -525,6 +532,7 @@ def scan_gcode_metadata(self, filename_dir: str): @QtCore.pyqtSlot(name="api_get_gcode_thumbnail") def get_gcode_thumbnail(self, filename_dir: str): + """Request gcode thumbnail""" if isinstance(filename_dir, str) is False or filename_dir is None: return False return self._ws.send_request( @@ -534,6 +542,7 @@ def get_gcode_thumbnail(self, filename_dir: str): @QtCore.pyqtSlot(str, str, name="api-delete-file") @QtCore.pyqtSlot(str, name="api-delete-file") def delete_file(self, filename: str, root_dir: str = "gcodes"): + """Request file deletion""" filepath = f"{root_dir}/{filename}" return self._ws.send_request( method="server.files.delete_file", @@ -542,7 +551,7 @@ def delete_file(self, filename: str, root_dir: str = "gcodes"): @QtCore.pyqtSlot(str, str, name="api-file_download") def download_file(self, root: str, filename: str): - """download_file Retrieves file *filename* at root *root*, the filename must include the relative path if + """Retrieves file *filename* at root *root*, the filename must include the relative path if it is not in the root folder Args: @@ -561,6 +570,7 @@ def download_file(self, root: str, filename: str): @QtCore.pyqtSlot(str, name="api-get-dir-info") @QtCore.pyqtSlot(str, bool, name="api-get-dir-info") def get_dir_information(self, directory: str = "", extended: bool = True): + """Request directory information""" if not isinstance(directory, str): return False return self._ws.send_request( @@ -569,6 +579,7 @@ def get_dir_information(self, directory: str = "", extended: bool = True): ) def create_directory(self, directory: str): + """Create directory""" if isinstance(directory, str) is False or directory is None: return False return self._ws.send_request( @@ -579,6 +590,7 @@ def create_directory(self, directory: str): ) def delete_directory(self, directory: str): + """Delete directory""" if isinstance(directory, str) is False or directory is None: return False return self._ws.send_request( @@ -589,6 +601,7 @@ def delete_directory(self, directory: str): ) def move_file(self, source_dir: str, dest_dir: str): + """Move file""" if ( isinstance(source_dir, str) is False or isinstance(dest_dir, str) is False @@ -602,6 +615,7 @@ def move_file(self, source_dir: str, dest_dir: str): ) def copy_file(self, source_dir: str, dest_dir: str): + """Copy file""" if ( isinstance(source_dir, str) is False or isinstance(dest_dir, str) is False @@ -614,21 +628,19 @@ def copy_file(self, source_dir: str, dest_dir: str): params={"source": source_dir, "dest": dest_dir}, ) - def zip_archive(self, items: list): - raise NotImplementedError() - - # !Can implement a jog queueu - def list_announcements(self, include_dismissed: bool = False): + """Request available announcements""" return self._ws.send_request( method="server.announcements.list", params={"include_dismissed": include_dismissed}, ) def update_announcements(self): + """Request announcements update to moonraker""" return self._ws.send_request(method="server.announcements.update") def dismiss_announcements(self, entry_id: str, wake_time: int = 600): + """Dismiss announcements""" if ( isinstance(entry_id, str) is False or entry_id is None @@ -641,9 +653,11 @@ def dismiss_announcements(self, entry_id: str, wake_time: int = 600): ) def list_announcements_feeds(self): + """List announcement feeds""" return self._ws.send_request(method="server.announcements.feeds") def post_announcement_feed(self, announcement_name: str): + """Post annoucement feeds""" if isinstance(announcement_name, str) is False or announcement_name is None: return False return self._ws.send_request( @@ -652,6 +666,7 @@ def post_announcement_feed(self, announcement_name: str): ) def delete_announcement_feed(self, announcement_name: str): + """Delete announcement feeds""" if isinstance(announcement_name, str) is False or announcement_name is None: return False return self._ws.send_request( @@ -659,21 +674,20 @@ def delete_announcement_feed(self, announcement_name: str): params={"name": announcement_name}, ) - # * WEBCAM - def list_webcams(self): + """List available webcams""" return self._ws.send_request(method="server.webcams.list") def get_webcam_info(self, uid: str): + """Get webcamera information""" if isinstance(uid, str) is False or uid is None: return False return self._ws.send_request( method="server.webcams.get_info", params={"uid": uid} ) - # TODO: Can create a class that irs a URL type like i've done before to validate the links - # TODO: There are more options in this section, alot more options, later see if it's worth to implement or not def add_update_webcam(self, cam_name: str, snapshot_url: str, stream_url: str): + """Add or update webcamera""" if ( isinstance(cam_name, str) is False or isinstance(snapshot_url, str) is False @@ -693,6 +707,7 @@ def add_update_webcam(self, cam_name: str, snapshot_url: str, stream_url: str): ) def delete_webcam(self, uid: str): + """Delete webcamera""" if isinstance(uid, str) is False or uid is None: return False return self._ws.send_request( @@ -700,15 +715,18 @@ def delete_webcam(self, uid: str): ) def test_webcam(self, uid: str): + """Test webcamera connection""" if isinstance(uid, str) is False or uid is None: return False return self._ws.send_request(method="server.webcams.test", params={"uid": uid}) def list_notifiers(self): + """List configured notifiers""" return self._ws.send_request(method="server.notifiers.list") @QtCore.pyqtSlot(bool, name="update-status") def update_status(self, refresh: bool = False) -> bool: + """Get packages state""" return self._ws.send_request( method="machine.update.status", params={"refresh": refresh} ) @@ -716,6 +734,7 @@ def update_status(self, refresh: bool = False) -> bool: @QtCore.pyqtSlot(name="update-refresh") @QtCore.pyqtSlot(str, name="update-refresh") def refresh_update_status(self, name: str = "") -> bool: + """Refresh packages state""" if not isinstance(name, str) or not name: return False return self._ws.send_request( @@ -724,29 +743,35 @@ def refresh_update_status(self, name: str = "") -> bool: @QtCore.pyqtSlot(name="update-full") def full_update(self) -> bool: + """Issue full upgrade to all packages""" return self._ws.send_request(method="machine.update.full") @QtCore.pyqtSlot(name="update-moonraker") def update_moonraker(self) -> bool: + """Issue moonraker update""" return self._ws.send_request(method="machine.update.moonraker") @QtCore.pyqtSlot(name="update-klipper") def update_klipper(self) -> bool: + """Issue klipper update""" return self._ws.send_request(method="machine.update.klipper") @QtCore.pyqtSlot(str, name="update-client") def update_client(self, client_name: str = "") -> bool: + """Issue client update""" if not isinstance(client_name, str) or not client_name: return False return self._ws.send_request(method="machine.update.client") @QtCore.pyqtSlot(name="update-system") def update_system(self): + """Issue system update""" return self._ws.send_request(method="machine.update.system") @QtCore.pyqtSlot(str, name="recover-repo") @QtCore.pyqtSlot(str, bool, name="recover-repo") def recover_corrupt_repo(self, name: str, hard: bool = False): + """Issue package recovery""" if isinstance(name, str) is False or name is None: return False return self._ws.send_request( @@ -756,54 +781,29 @@ def recover_corrupt_repo(self, name: str, hard: bool = False): @QtCore.pyqtSlot(str, name="rollback-update") def rollback_update(self, name: str): + """Issue rollback update""" if not isinstance(name, str) or not name: return False return self._ws.send_request( method="machine,update.rollback", params={"name": name} ) - # If moonraker [history] is configured def history_list(self, limit, start, since, before, order): - # TODO: + """Request Job history list""" raise NotImplementedError - return self._ws.send_request( - method="server.history.list", - params={ - "limit": limit, - "start": start, - "since": since, - "before": before, - "order": order, - }, - ) def history_job_totals(self): + """Request total job history""" raise NotImplementedError - return self._ws.send_request(method="server.history.totals") def history_reset_totals(self): + """Request history reset""" raise NotImplementedError - return self._ws.send_request(method="server.history.reset_totals") def history_get_job(self, uid: str): + """Request job history""" raise NotImplementedError - return self._ws.send_request( - method="server.history.get_job", params={"uid": uid} - ) def history_delete_job(self, uid: str): + """Request delete job history""" raise NotImplementedError - # It is possible to replace the uid argument with all=true to delete all jobs in the history database. - return self._ws.send_request( - method="server.history.delete_job", params={"uid": uid} - ) - - -############################################################################################################################ -# TODO: WEBSOCKET NOTIFICATIONS - -# TODO: Pass the logger object instanteation to another class so that the main window defines and calls it -# TODO: make host, port and websocket name not static but a argument that can be feed in the class -# TODO: Create websocket connection for each user login, which means different api keys for each user - -# TEST: Try and use multiprocessing as it sidesteps the GIL diff --git a/BlocksScreen/lib/moonrest.py b/BlocksScreen/lib/moonrest.py index d85b5425..1e43552a 100644 --- a/BlocksScreen/lib/moonrest.py +++ b/BlocksScreen/lib/moonrest.py @@ -57,6 +57,7 @@ def __init__(self, host: str = "localhost", port: int = 7125, api_key=False): @property def build_endpoint(self): + """Build connection endpoint""" return f"http://{self._host}:{self._port}" def get_oneshot_token(self): @@ -93,11 +94,8 @@ def firmware_restart(self): """ return self.post_request(method="printer/firmware_restart") - def delete_request(self): - # TODO: Create a delete request, so the user is able to delete files from the pi, can also be made with websockets - pass - def post_request(self, method, data=None, json=None, json_response=True): + """POST request""" return self._request( request_type="post", method=method, @@ -107,6 +105,7 @@ def post_request(self, method, data=None, json=None, json_response=True): ) def get_request(self, method, json=True, timeout=timeout): + """GET request""" return self._request( request_type="get", method=method, @@ -123,8 +122,6 @@ def _request( json_response=True, timeout=timeout, ): - # TODO: Need to check if the header is actually correct or not - # TEST: Test the reliability of this _url = f"{self.build_endpoint}/{method}" _headers = {"x-api-key": self._api_key} if self._api_key else {} try: diff --git a/BlocksScreen/lib/network.py b/BlocksScreen/lib/network.py index ca8a5ded..312b60e8 100644 --- a/BlocksScreen/lib/network.py +++ b/BlocksScreen/lib/network.py @@ -1,6 +1,5 @@ import asyncio import enum -import hashlib import logging import threading import typing @@ -23,6 +22,8 @@ def __init__(self, error): class SdbusNetworkManagerAsync(QtCore.QObject): class ConnectionPriority(enum.Enum): + """Connection priorities""" + HIGH = 90 MEDIUM = 50 LOW = 20 @@ -111,6 +112,7 @@ def close(self) -> None: self.loop.close() async def listener_monitor(self) -> None: + """Monitor for NetworkManager properties""" try: self._listeners_running = True @@ -152,6 +154,7 @@ async def _nm_properties_listener(self) -> None: logging.error(f"Exception on Network Manager state listener: {e}") def check_nm_state(self) -> typing.Union[str, None]: + """Check NetworkManager state""" if not self.nm: return future = asyncio.run_coroutine_threadsafe(self.nm.state.get_async(), self.loop) @@ -194,6 +197,11 @@ def check_connectivity(self) -> str: return "" def check_wifi_interface(self) -> bool: + """Check if wifi interface is set + + Returns: + bool: true if it is. False otherwise + """ return bool(self.primary_wifi_interface) def get_available_interfaces(self) -> typing.Union[typing.List[str], None]: @@ -266,6 +274,7 @@ async def _toggle_networking(self, value: bool = True) -> None: logger.error(f"Exception Caught when toggling network : {result}") def disable_networking(self) -> None: + """Disable networking""" if not (self.primary_wifi_interface and self.primary_wired_interface): return if self.primary_wifi_interface == "/" and self.primary_wired_interface == "/": @@ -273,6 +282,7 @@ def disable_networking(self) -> None: asyncio.run_coroutine_threadsafe(self._toggle_networking(False), self.loop) def activate_networking(self) -> None: + """Activate networking""" if not (self.primary_wifi_interface and self.primary_wired_interface): return if self.primary_wifi_interface == "/" and self.primary_wired_interface == "/": @@ -327,7 +337,6 @@ def hotspot_enabled(self) -> typing.Optional["bool"]: Returns: bool: True if Hotspot is activated, False otherwise. """ - # REFACTOR: untested for all cases return bool(self.hotspot_ssid == self.get_current_ssid()) def get_wired_interfaces(self) -> typing.List[dbusNm.NetworkDeviceWired]: @@ -415,6 +424,11 @@ async def _gather_ssid(self) -> str: return "" def get_current_ssid(self) -> str: + """Get current ssid + + Returns: + str: ssid address + """ try: future = asyncio.run_coroutine_threadsafe(self._gather_ssid(), self.loop) return future.result(timeout=5) @@ -457,7 +471,7 @@ def get_current_ip_addr(self) -> str: addr_data = addr_data_fut.result(timeout=2) return [address_data["address"][1] for address_data in addr_data][0] except IndexError as e: - logger.error(f"List out of index %s", e) + logger.error("List out of index %s", e) return "" async def _gather_primary_interface( @@ -508,7 +522,6 @@ def get_primary_interface( If there is no wireless interface and no active connection return the first wired interface that is not (lo). - ### `TODO: Completely blocking and should be refactored` Returns: typing.List: """ @@ -607,6 +620,7 @@ async def _get_available_networks(self) -> typing.Union[typing.Dict, None]: return {} def get_available_networks(self) -> typing.Union[typing.Dict, None]: + """Get available networks""" future = asyncio.run_coroutine_threadsafe( self._get_available_networks(), self.loop ) @@ -1244,9 +1258,11 @@ def delete_network(self, ssid: str) -> None: logging.debug(f"Caught Exception while deleting network {ssid}: {e}") def get_hotspot_ssid(self) -> str: + """Get current hotspot ssid""" return self.hotspot_ssid def deactivate_connection(self, connection_path) -> None: + """Deactivate a connection, by connection path""" if not self.nm: return if not self.primary_wifi_interface: @@ -1269,6 +1285,7 @@ def deactivate_connection(self, connection_path) -> None: ) def deactivate_connection_by_ssid(self, ssid: str) -> None: + """Deactivate connection by ssid""" if not self.nm: return if not self.primary_wifi_interface: @@ -1287,6 +1304,12 @@ def deactivate_connection_by_ssid(self, ssid: str) -> None: def create_hotspot( self, ssid: str = "PrinterHotspot", password: str = "123456789" ) -> None: + """Create hostpot + + Args: + ssid (str, optional): Hotspot ssid. Defaults to "PrinterHotspot". + password (str, optional): connection password. Defaults to "123456789". + """ if self.is_known(ssid): self.delete_network(ssid) logger.debug("old hotspot deleted") @@ -1339,6 +1362,12 @@ def create_hotspot( def set_network_priority( self, ssid: str, priority: ConnectionPriority = ConnectionPriority.LOW ) -> None: + """Set network priority + + Args: + ssid (str): connection ssid + priority (ConnectionPriority, optional): Priority. Defaults to ConnectionPriority.LOW. + """ if not self.nm: return if not self.is_known(ssid): @@ -1408,4 +1437,4 @@ def update_connection_settings( if password != self.hotspot_password and password: self.hotspot_password = password except Exception as e: - logger.error(f"Caught Exception while updating network: %s", e) + logger.error("Caught Exception while updating network: %s", e) diff --git a/BlocksScreen/lib/panels/controlTab.py b/BlocksScreen/lib/panels/controlTab.py index a66ba1e6..bef67b3f 100644 --- a/BlocksScreen/lib/panels/controlTab.py +++ b/BlocksScreen/lib/panels/controlTab.py @@ -14,6 +14,7 @@ from lib.panels.widgets.popupDialogWidget import Popup + class ControlTab(QtWidgets.QStackedWidget): """Printer Control Stacked Widget""" @@ -108,12 +109,6 @@ def __init__( partial(self.change_page, self.indexOf(self.panel.temperature_page)) ) self.panel.cp_switch_print_core_btn.clicked.connect(self.show_swapcore) - # self.panel.cp_printer_settings_btn.clicked.connect( - # partial( - # self.change_page, - # self.indexOf(self.panel.printer_settings_page), - # ) - # ) self.panel.cp_nozzles_calibration_btn.clicked.connect( partial(self.change_page, self.indexOf(self.probe_helper_page)) ) @@ -258,9 +253,7 @@ def __init__( ) ) - self.panel.cp_z_tilt_btn.clicked.connect( - lambda: self.handle_ztilt() - ) + self.panel.cp_z_tilt_btn.clicked.connect(lambda: self.handle_ztilt()) self.printcores_page.pc_accept.clicked.connect(self.handle_swapcore) @@ -274,9 +267,7 @@ def __init__( self.panel.cooldown_btn.hide() self.panel.cp_switch_print_core_btn.hide() - - def handle_printcoreupdate(self, value:dict): - + def handle_printcoreupdate(self, value: dict): if value["swapping"] == "idle": return @@ -289,14 +280,10 @@ def handle_printcoreupdate(self, value:dict): ) if value["swapping"] == "unloading": self.loadpage.set_status_message("Unloading print core") - + if value["swapping"] == "cleaning": self.loadpage.set_status_message("Cleaning print core") - - - - def _handle_gcode_response(self, messages: list): """Handle gcode response for Z-tilt adjustment""" pattern = r"Retries:\s*(\d+)/(\d+).*?range:\s*([\d.]+)\s*tolerance:\s*([\d.]+)" @@ -305,7 +292,11 @@ def _handle_gcode_response(self, messages: list): if not msg_list: continue - if "Retries:" in msg_list and "range:" in msg_list and "tolerance:" in msg_list: + if ( + "Retries:" in msg_list + and "range:" in msg_list + and "tolerance:" in msg_list + ): print("Match candidate:", msg_list) match = re.search(pattern, msg_list) print("Regex match:", match) @@ -327,7 +318,6 @@ def _handle_gcode_response(self, messages: list): f"Retries: {retries_done}/{retries_total} | Range: {probed_range:.6f} | Tolerance: {tolerance:.6f}" ) - def handle_ztilt(self): """Handle Z-Tilt Adjustment""" self.loadpage.show() @@ -351,7 +341,6 @@ def show_swapcore(self): self.loadpage.show() self.loadpage.set_status_message("Preparing to swap print core") - def handle_swapcore(self): """Handle swap printcore routine finish""" self.printcores_page.setText("Executing \n Firmware Restart") @@ -521,7 +510,7 @@ def on_toolhead_update(self, field: str, values: list) -> None: self.panel.mva_y_value_label.setText(f"{values[1]:.2f}") self.panel.mva_z_value_label.setText(f"{values[2]:.3f}") - if values[0] == "252,50" and values[1] == "250" and values[2] == "50": + if values[0] == "252,50" and values[1] == "250" and values[2] == "50": self.loadpage.hide self.toolhead_info.update({f"{field}": values}) diff --git a/BlocksScreen/lib/panels/filamentTab.py b/BlocksScreen/lib/panels/filamentTab.py index ceb77a2a..4ab358fa 100644 --- a/BlocksScreen/lib/panels/filamentTab.py +++ b/BlocksScreen/lib/panels/filamentTab.py @@ -1,5 +1,4 @@ import enum -import typing from functools import partial @@ -52,7 +51,6 @@ def __init__(self, parent: QtWidgets.QWidget, printer: Printer, ws, /) -> None: partial(self.change_page, self.indexOf(self.panel.load_page)) ) self.panel.custom_filament_header_back_btn.clicked.connect(self.back_button) - # REFACTOR self.panel.load_custom_btn.clicked.connect(partial(self.change_page, 2)) self.panel.load_custom_btn.hide() self.panel.load_header_back_button.clicked.connect(self.back_button) self.panel.load_pla_btn.clicked.connect( @@ -95,9 +93,7 @@ def __init__(self, parent: QtWidgets.QWidget, printer: Printer, ws, /) -> None: @QtCore.pyqtSlot(str, float, name="on_print_stats_update") @QtCore.pyqtSlot(str, str, name="on_print_stats_update") def on_print_stats_update(self, field: str, value: dict | float | str) -> None: - """ - unblocks tabs if on standby - """ + """Handle print stats object update""" if isinstance(value, str): if "state" in field: if value in ("standby"): @@ -106,6 +102,7 @@ def on_print_stats_update(self, field: str, value: dict | float | str) -> None: @QtCore.pyqtSlot(str, str, bool, name="on_filament_sensor_update") def on_filament_sensor_update(self, sensor_name: str, parameter: str, value: bool): + """Handle filament sensor object update""" if parameter == "filament_detected": if not isinstance(value, bool): self._filament_state = self.FilamentStates.UNKNOWN @@ -126,6 +123,7 @@ def on_filament_sensor_update(self, sensor_name: str, parameter: str, value: boo def on_extruder_update( self, extruder_name: str, field: str, new_value: float ) -> None: + """Handle extruder update""" if not self.isVisible: return @@ -134,16 +132,17 @@ def on_extruder_update( self.loadscreen.set_status_message("Extruder heated up \n Please wait") return if field == "temperature": - self.current_temp = round(new_value, 0) # somehow this works + self.current_temp = round(new_value, 0) self.loadscreen.set_status_message( f"Heating up ({new_value}/{self.target_temp}) \n Please wait" ) if field == "target": - self.target_temp = round(new_value, 0) # somehow this works again + self.target_temp = round(new_value, 0) self.loadscreen.set_status_message("Heating up \n Please wait") @QtCore.pyqtSlot(bool, name="on_load_filament") def on_load_filament(self, status: bool): + """Handle load filament object updated""" if self.loadignore: self.loadignore = False return @@ -160,6 +159,7 @@ def on_load_filament(self, status: bool): @QtCore.pyqtSlot(bool, name="on_unload_filament") def on_unload_filament(self, status: bool): + """Handle unload filament object updated""" if self.unloadignore: self.unloadignore = False return @@ -177,6 +177,7 @@ def on_unload_filament(self, status: bool): @QtCore.pyqtSlot(int, int, name="load_filament") def load_filament(self, toolhead: int = 0, temp: int = 220) -> None: + """Handle load filament buttons clicked""" if not self.isVisible: return @@ -197,6 +198,7 @@ def load_filament(self, toolhead: int = 0, temp: int = 220) -> None: @QtCore.pyqtSlot(str, int, name="unload_filament") def unload_filament(self, toolhead: int = 0, temp: int = 220) -> None: + """Handle unload filament button clicked""" if not self.isVisible: return @@ -218,6 +220,7 @@ def unload_filament(self, toolhead: int = 0, temp: int = 220) -> None: self.run_gcode.emit(f"UNLOAD_FILAMENT TEMPERATURE={temp}") def handle_filament_state(self): + """Handle ui changes on filament states""" if self._filament_state == self.FilamentStates.LOADED: self.panel.filament_page_load_btn.setDisabled(True) self.panel.filament_page_load_btn.setDisabled(False) @@ -233,29 +236,22 @@ def filament_state(self): return self._filament_state def change_page(self, index): + """Issue a page change""" self.request_change_page.emit(1, index) def back_button(self): + """Go back a page""" self.request_back.emit() - def sizeHint(self) -> QtCore.QSize: - return super().sizeHint() - def paintEvent(self, a0: QtGui.QPaintEvent | None) -> None: + """Widget painting""" if self.panel.load_page.isVisible() and self.toolhead_count == 1: self.panel.load_header_page_title.setText("Load Toolhead") if a0 is not None: return super().paintEvent(a0) - def removeWidget(self, w: QtWidgets.QWidget | None) -> None: - if w is not None: - return super().removeWidget(w) - - def resizeEvent(self, a0: QtGui.QResizeEvent | None) -> None: - if a0 is not None: - return super().resizeEvent(a0) - def find_routine_objects(self): + """Check if printer has load/unload printer objects""" if not self.printer: return diff --git a/BlocksScreen/lib/panels/instructionsWindow.py b/BlocksScreen/lib/panels/instructionsWindow.py deleted file mode 100644 index cce27c46..00000000 --- a/BlocksScreen/lib/panels/instructionsWindow.py +++ /dev/null @@ -1,30 +0,0 @@ -from PyQt6.QtWidgets import QStackedWidget, QWidget -from PyQt6 import QtCore -import typing - -from lib.ui.instructionsWindow_ui import Ui_utilitiesStackedWidget - - -# TODO: Complete this panel - -class InstructionsWindow(QStackedWidget): - - def __init__(self, parent: typing.Optional[QWidget] = ...) -> None: - super().__init__(parent) - - self.panel = Ui_utilitiesStackedWidget() - self.panel.setupUi(self) - # self.show() - - self.index_stack = [] - - # Connecting the print_btn.clicked event to the change_page method - #self.panel.main_print_btn.clicked.connect(self.change_page) - #self.panel.files_back_folder_btn.clicked.connect(self.change_page) - - - def change_page(self, int): - self.setCurrentIndex(int) - self.index_stack.append(self.currentIndex()) - - \ No newline at end of file diff --git a/BlocksScreen/lib/panels/mainWindow.py b/BlocksScreen/lib/panels/mainWindow.py index f381febb..b7921e55 100644 --- a/BlocksScreen/lib/panels/mainWindow.py +++ b/BlocksScreen/lib/panels/mainWindow.py @@ -35,11 +35,12 @@ def api_handler(func): """Decorator for methods that handle api responses""" def wrapper(*args, **kwargs): + """Decorator for api_handler""" try: result = func(*args, **kwargs) return result except Exception as e: - _logger.error(f"Caught Exception in %s : %s ", func.__name__, e) + _logger.error("Caught Exception in %s : %s ", func.__name__, e) raise return wrapper @@ -156,25 +157,27 @@ def __init__(self): # @ Start websocket connection with moonraker self.bo_ws_startup.emit() self.reset_tab_indexes() + @QtCore.pyqtSlot(name="on-cancel-print") def on_cancel_print(self): - self.enable_tab_bar() - self.ui.extruder_temp_display.clicked.disconnect() - self.ui.bed_temp_display.clicked.disconnect() - self.ui.filament_type_icon.setDisabled(False) - self.ui.nozzle_size_icon.setDisabled(False) - self.ui.extruder_temp_display.clicked.connect( - lambda: self.global_change_page( - self.ui.main_content_widget.indexOf(self.ui.controlTab), - self.controlPanel.indexOf(self.controlPanel.panel.temperature_page), - ) + """Slot for cancel print signal""" + self.enable_tab_bar() + self.ui.extruder_temp_display.clicked.disconnect() + self.ui.bed_temp_display.clicked.disconnect() + self.ui.filament_type_icon.setDisabled(False) + self.ui.nozzle_size_icon.setDisabled(False) + self.ui.extruder_temp_display.clicked.connect( + lambda: self.global_change_page( + self.ui.main_content_widget.indexOf(self.ui.controlTab), + self.controlPanel.indexOf(self.controlPanel.panel.temperature_page), ) - self.ui.bed_temp_display.clicked.connect( - lambda: self.global_change_page( - self.ui.main_content_widget.indexOf(self.ui.controlTab), - self.controlPanel.indexOf(self.controlPanel.panel.temperature_page), - ) + ) + self.ui.bed_temp_display.clicked.connect( + lambda: self.global_change_page( + self.ui.main_content_widget.indexOf(self.ui.controlTab), + self.controlPanel.indexOf(self.controlPanel.panel.temperature_page), ) + ) @QtCore.pyqtSlot(bool, name="update-available") def on_update_available(self, state: bool = False): @@ -492,14 +495,7 @@ def _handle_notify_klippy_message(self, method, data, metadata) -> None: @api_handler def _handle_notify_filelist_changed_message(self, method, data, metadata) -> None: """Handle websocket file list messages""" - _file_change_list = data.get("params") - if _file_change_list: - fileaction = _file_change_list[0].get("action") - filepath = ( - _file_change_list[0].get("item").get("path") - ) # TODO : NOTIFY_FILELIST_CHANGED, I DON'T KNOW IF I REALLY WANT TO SEND NOTIFICATIONS ON FILE CHANGES. ... - # self.file_data.request_file_list.emit() @api_handler def _handle_notify_service_state_changed_message( diff --git a/BlocksScreen/lib/panels/networkWindow.py b/BlocksScreen/lib/panels/networkWindow.py index 5df4b81d..57617efe 100644 --- a/BlocksScreen/lib/panels/networkWindow.py +++ b/BlocksScreen/lib/panels/networkWindow.py @@ -1,6 +1,6 @@ import logging import typing -import subprocess +import subprocess # nosec: B404 from functools import partial from lib.network import SdbusNetworkManagerAsync @@ -12,6 +12,7 @@ logger = logging.getLogger("logs/BlocksScreen.log") + class BuildNetworkList(QtCore.QThread): """Retrieves information from sdbus interface about scanned networks""" @@ -233,7 +234,7 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: partial( self.panel.saved_connection_change_password_field.setEchoMode, QtWidgets.QLineEdit.EchoMode.Normal, - ) + ) ) self.panel.saved_connection_change_password_view.released.connect( partial( @@ -315,13 +316,8 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") ) - - self.panel.network_activate_btn.clicked.connect( - lambda: self.saved_wifi_option_selected() - ) - self.panel.network_delete_btn.clicked.connect( - lambda: self.saved_wifi_option_selected() - ) + self.panel.network_activate_btn.clicked.connect(self.saved_wifi_option_selected) + self.panel.network_delete_btn.clicked.connect(self.saved_wifi_option_selected) self.network_list_worker.build() self.request_network_scan.emit() @@ -356,30 +352,41 @@ def __init__(self, parent: typing.Optional[QtWidgets.QWidget], /) -> None: ) def saved_wifi_option_selected(self): + """Handle connect/delete network button clicks""" _sender = self.sender() - self.panel.wifi_button.toggle_button.state = self.panel.wifi_button.toggle_button.State.ON - self.panel.hotspot_button.toggle_button.state = self.panel.hotspot_button.toggle_button.State.OFF + self.panel.wifi_button.toggle_button.state = ( + self.panel.wifi_button.toggle_button.State.ON + ) + self.panel.hotspot_button.toggle_button.state = ( + self.panel.hotspot_button.toggle_button.State.OFF + ) if _sender == self.panel.network_delete_btn: - self.sdbus_network.delete_network(self.panel.saved_connection_network_name.text()) + self.sdbus_network.delete_network( + self.panel.saved_connection_network_name.text() + ) self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) elif _sender == self.panel.network_activate_btn: self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) - self.sdbus_network.connect_network(self.panel.saved_connection_network_name.text()) + self.sdbus_network.connect_network( + self.panel.saved_connection_network_name.text() + ) self.info_box_load(True) - def on_show_keyboard(self, panel: QtWidgets.QWidget, field: QtWidgets.QLineEdit): + """Handle keyboard show""" self.previousPanel = panel self.currentField = field self.qwerty.set_value(field.text()) self.setCurrentIndex(self.indexOf(self.qwerty)) def on_qwerty_go_back(self): + """Hide keyboard""" self.setCurrentIndex(self.indexOf(self.previousPanel)) def on_qwerty_value_selected(self, value: str): + """Handle keyboard value input""" self.setCurrentIndex(self.indexOf(self.previousPanel)) if hasattr(self, "currentField") and self.currentField: self.currentField.setText(value) @@ -390,16 +397,15 @@ def info_box_load(self, toggle: bool = False) -> None: Sets a 30-second timeout to handle loading failures. """ self._show_loadscreen(toggle) - + self.panel.wifi_button.setEnabled(not toggle) self.panel.hotspot_button.setEnabled(not toggle) - + if toggle: if self._load_timer.isActive(): self._load_timer.stop() self._load_timer.start(30000) - def _handle_load_timeout(self): """ Logic to execute if the loading screen is still visible after 30 seconds.< @@ -416,12 +422,10 @@ def _handle_load_timeout(self): else: message = "Loading timed out.\n Please check your connection \n and try again." - - self.panel.mn_info_box.setText(message) self._show_loadscreen(False) self._expand_infobox(True) - + hotspot_btn.setEnabled(True) wifi_btn.setEnabled(True) @@ -448,10 +452,11 @@ def _show_loadscreen(self, toggle: bool = False): @QtCore.pyqtSlot(object, name="stateChange") def on_toggle_state(self, new_state) -> None: + """Handle toggle button changes""" sender_button = self.sender() wifi_btn = self.panel.wifi_button.toggle_button hotspot_btn = self.panel.hotspot_button.toggle_button - is_sender_now_on = (new_state == sender_button.State.ON) + is_sender_now_on = new_state == sender_button.State.ON _old_hotspot = None saved_network = self.sdbus_network.get_saved_networks() @@ -463,8 +468,13 @@ def on_toggle_state(self, new_state) -> None: if saved_network: try: ssid = next( - (n["ssid"] for n in saved_network if "ap" not in n['mode']and n["signal"] != 0), - None) + ( + n["ssid"] + for n in saved_network + if "ap" not in n["mode"] and n["signal"] != 0 + ), + None, + ) self.sdbus_network.connect_network(str(ssid)) except Exception as e: @@ -532,7 +542,7 @@ def evaluate_network_state(self, nm_state: str = "") -> None: break if _old_hotspot: self.panel.hotspot_name_input_field.setText(_old_hotspot["ssid"]) - + connection = self.sdbus_network.check_connectivity() if connection == "FULL": self.panel.wifi_button.toggle_button.state = ( @@ -547,23 +557,22 @@ def evaluate_network_state(self, nm_state: str = "") -> None: ) self.panel.hotspot_button.toggle_button.state = ( self.panel.hotspot_button.toggle_button.State.ON - ) - + ) if not self.sdbus_network.check_wifi_interface(): return if hotspot_btn.state == hotspot_btn.State.ON: - ipv4_addr = self.get_hotspot_ip_via_shell("wlan0") + ipv4_addr = self.get_hotspot_ip_via_shell() self.panel.netlist_ssuid.setText(self.panel.hotspot_name_input_field.text()) - self.panel.netlist_ip.setText(f"IP: {ipv4_addr or 'No IP Address'}") + self.panel.netlist_ip.setText(f"IP: {ipv4_addr or 'No IP Address'}") self.panel.netlist_strength.setText("--") - + self.panel.netlist_security.setText("--") - + self.panel.mn_info_box.setText("Hotspot On") if wifi_btn.state == wifi_btn.State.ON: @@ -589,7 +598,6 @@ def evaluate_network_state(self, nm_state: str = "") -> None: self.panel.hotspot_button.setEnabled(True) self.repaint() - if ( wifi_btn.state == wifi_btn.State.OFF and hotspot_btn.state == hotspot_btn.State.OFF @@ -600,37 +608,54 @@ def evaluate_network_state(self, nm_state: str = "") -> None: "Network connection required.\n\nConnect to Wi-Fi\nor\nTurn on Hotspot" ) - def get_hotspot_ip_via_shell(self, interface: str): + def get_hotspot_ip_via_shell(self): """ Executes a shell command to retrieve the IPv4 address for a specified interface. - Args: - interface: The name of the hotspot interface (e.g., 'wlan0'). Returns: The IP address string (e.g., '10.42.0.1') or None if not found. """ - command = ( - f"ip a show {interface} | grep 'inet ' | awk '{{print $2}}' | cut -d/ -f1" - ) + command = [ + "ip", + "a", + "show", + "wlan0", + " |", + "grep", + " 'inet '", + "|", + "awk", + " '{{print $2}}'", + "|", + "cut", + "-d/", + "-f1", + ] try: - result = subprocess.run( + result = subprocess.run( # nosec: B603 command, - shell=True, capture_output=True, text=True, check=True, timeout=5, ) - ip_addr = result.stdout.strip() if ip_addr and len(ip_addr.split(".")) == 4: return ip_addr - except Exception as e: - print(f"An unexpected error occurred: {e}") - - return None + except subprocess.CalledProcessError as e: + logging.error( + "Caught exception (exit code %d) failed to run command: %s \nStderr: %s", + e.returncode, + command, + e.stderr.strip(), + ) + raise + except subprocess.TimeoutExpired as e: + logging.error("Caught exception, failed to run command %s", e) + raise def close(self) -> bool: + """Close class, close network module""" self.sdbus_network.close() return super().close() @@ -656,14 +681,17 @@ def _expand_infobox(self, toggle: bool = False) -> None: @QtCore.pyqtSlot(str, name="delete-network") def delete_network(self, ssid: str) -> None: + """Delete network""" self.sdbus_network.delete_network(ssid=ssid) @QtCore.pyqtSlot(name="rescan-networks") def rescan_networks(self) -> None: + """Rescan for networks""" self.sdbus_network.rescan_networks() @QtCore.pyqtSlot(name="handle-hotspot-back") def handle_hotspot_back(self) -> None: + """Handle go back a page from hotspot page""" if ( self.panel.hotspot_password_input_field.text() != self.sdbus_network.hotspot_password @@ -709,7 +737,12 @@ def add_network(self) -> None: if not error_msg: # Assume it was a success QtCore.QTimer().singleShot(5000, self.network_list_worker.build) - QtCore.QTimer().singleShot(5000, lambda: self.sdbus_network.connect_network(self.panel.add_network_network_label.text())) + QtCore.QTimer().singleShot( + 5000, + lambda: self.sdbus_network.connect_network( + self.panel.add_network_network_label.text() + ), + ) self.info_box_load(True) self.setCurrentIndex(self.indexOf(self.panel.main_network_page)) self.panel.add_network_validation_button.setEnabled(True) @@ -731,7 +764,6 @@ def add_network(self) -> None: self.panel.add_network_validation_button.setEnabled(True) self.panel.add_network_validation_button.repaint() self.popup.new_message(message_type=Popup.MessageType.ERROR, message=message) - @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name="ssid_item_clicked") def ssid_item_clicked(self, item: QtWidgets.QListWidgetItem) -> None: @@ -765,6 +797,7 @@ def update_network( password: typing.Union[str, None], new_ssid: typing.Union[str, None], ) -> None: + """Update network information""" if not self.sdbus_network.is_known(ssid): return @@ -778,6 +811,7 @@ def update_network( @QtCore.pyqtSlot(list, name="finished-network-list-build") def handle_network_list(self, data: typing.List[typing.Tuple]) -> None: + """Handle available network list update""" scroll_bar_position = self.network_list_widget.verticalScrollBar().value() self.network_list_widget.blockSignals(True) self.network_list_widget.clear() diff --git a/BlocksScreen/lib/panels/printTab.py b/BlocksScreen/lib/panels/printTab.py index d899c5e1..98301283 100644 --- a/BlocksScreen/lib/panels/printTab.py +++ b/BlocksScreen/lib/panels/printTab.py @@ -42,15 +42,15 @@ class PrintTab(QtWidgets.QStackedWidget): """ - request_query_print_stats: typing.ClassVar[QtCore.pyqtSignal] = ( - QtCore.pyqtSignal(dict, name="request_query_print_stats") + request_query_print_stats: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + dict, name="request_query_print_stats" ) request_back: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( name="request-back" ) - request_change_page: typing.ClassVar[QtCore.pyqtSignal] = ( - QtCore.pyqtSignal(int, int, name="request_change_page") + request_change_page: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + int, int, name="request_change_page" ) run_gcode_signal: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( @@ -79,7 +79,6 @@ def __init__( self.gcode_path = os.path.expanduser("~/printer_data/gcodes") self.setMouseTracking(True) - self.sliderPage = SliderPage(self) self.addWidget(self.sliderPage) self.sliderPage.request_back.connect(self.back_button) @@ -95,7 +94,6 @@ def __init__( self.dialogPage = DialogPage(self) - self.confirmPage_widget = ConfirmWidget(self) self.addWidget(self.confirmPage_widget) self.confirmPage_widget.back_btn.clicked.connect(self.back_button) @@ -124,9 +122,7 @@ def __init__( self.filesPage_widget.request_dir_info[str].connect( self.file_data.request_dir_info[str] ) - self.filesPage_widget.request_dir_info.connect( - self.file_data.request_dir_info - ) + self.filesPage_widget.request_dir_info.connect(self.file_data.request_dir_info) self.file_data.on_file_list.connect(self.filesPage_widget.on_file_list) self.jobStatusPage_widget = JobStatusWidget(self) self.addWidget(self.jobStatusPage_widget) @@ -146,12 +142,8 @@ def __init__( ) self.file_data.fileinfo.connect(self.jobStatusPage_widget.on_fileinfo) self.jobStatusPage_widget.print_start.connect(self.ws.api.start_print) - self.jobStatusPage_widget.print_resume.connect( - self.ws.api.resume_print - ) - self.jobStatusPage_widget.print_cancel.connect( - self.handle_cancel_print - ) + self.jobStatusPage_widget.print_resume.connect(self.ws.api.resume_print) + self.jobStatusPage_widget.print_cancel.connect(self.handle_cancel_print) self.jobStatusPage_widget.print_pause.connect(self.ws.api.pause_print) self.jobStatusPage_widget.request_query_print_stats.connect( self.ws.api.object_query @@ -176,15 +168,9 @@ def __init__( self.jobStatusPage_widget.on_print_stats_update ) - self.printer.print_stats_update[str, str].connect( - self.on_print_stats_update - ) - self.printer.print_stats_update[str, dict].connect( - self.on_print_stats_update - ) - self.printer.print_stats_update[str, float].connect( - self.on_print_stats_update - ) + self.printer.print_stats_update[str, str].connect(self.on_print_stats_update) + self.printer.print_stats_update[str, dict].connect(self.on_print_stats_update) + self.printer.print_stats_update[str, float].connect(self.on_print_stats_update) self.printer.gcode_move_update[str, list].connect( self.jobStatusPage_widget.on_gcode_move_update @@ -222,12 +208,12 @@ def __init__( self.tune_page.request_sliderPage[str, int, "PyQt_PyObject"].connect( self.on_slidePage_request ) - self.tune_page.request_sliderPage[ - str, int, "PyQt_PyObject", int, int - ].connect(self.on_slidePage_request) - self.tune_page.request_numpad[ - str, int, "PyQt_PyObject", int, int - ].connect(self.on_numpad_request) + self.tune_page.request_sliderPage[str, int, "PyQt_PyObject", int, int].connect( + self.on_slidePage_request + ) + self.tune_page.request_numpad[str, int, "PyQt_PyObject", int, int].connect( + self.on_numpad_request + ) self.tune_page.request_numpad[ str, int, @@ -262,20 +248,14 @@ def __init__( self.run_gcode_signal.connect(self.ws.api.run_gcode) - self.confirmPage_widget.on_delete.connect( - self.delete_file - ) + self.confirmPage_widget.on_delete.connect(self.delete_file) - self.change_page( - self.indexOf(self.print_page) - ) # force set the initial page + self.change_page(self.indexOf(self.print_page)) # force set the initial page @QtCore.pyqtSlot(str, dict, name="on_print_stats_update") @QtCore.pyqtSlot(str, float, name="on_print_stats_update") @QtCore.pyqtSlot(str, str, name="on_print_stats_update") - def on_print_stats_update( - self, field: str, value: dict | float | str - ) -> None: + def on_print_stats_update(self, field: str, value: dict | float | str) -> None: """ unblocks tabs if on standby """ @@ -284,13 +264,8 @@ def on_print_stats_update( if value in ("standby"): self.on_cancel_print.emit() - - - @QtCore.pyqtSlot(str, int, "PyQt_PyObject", name="on_numpad_request") - @QtCore.pyqtSlot( - str, int, "PyQt_PyObject", int, int, name="on_numpad_request" - ) + @QtCore.pyqtSlot(str, int, "PyQt_PyObject", int, int, name="on_numpad_request") def on_numpad_request( self, name: str, @@ -299,6 +274,7 @@ def on_numpad_request( min_value: int = 0, max_value: int = 100, ) -> None: + """Handle numpad request""" self.numpadPage.value_selected.connect(callback) self.numpadPage.set_name(name) self.numpadPage.set_value(current_value) @@ -308,9 +284,7 @@ def on_numpad_request( self.change_page(self.indexOf(self.numpadPage)) @QtCore.pyqtSlot(str, int, "PyQt_PyObject", name="on_slidePage_request") - @QtCore.pyqtSlot( - str, int, "PyQt_PyObject", int, int, name="on_slidePage_request" - ) + @QtCore.pyqtSlot(str, int, "PyQt_PyObject", int, int, name="on_slidePage_request") def on_slidePage_request( self, name: str, @@ -319,6 +293,7 @@ def on_slidePage_request( min_value: int = 0, max_value: int = 100, ) -> None: + """Handle slider page request""" self.sliderPage.value_selected.connect(callback) self.sliderPage.set_name(name) self.sliderPage.set_slider_position(int(current_value)) @@ -326,28 +301,24 @@ def on_slidePage_request( self.sliderPage.set_slider_maximum(max_value) self.change_page(self.indexOf(self.sliderPage)) - def delete_file(self,direcotry:str,name:str): - self.directory:str = direcotry - self.filename:str = name + def delete_file(self, direcotry: str, name: str): + """Handle Delete file button clicked""" + self.directory: str = direcotry + self.filename: str = name self.dialogPage.set_message("Are you sure you want to delete this file?") self.dialogPage.button_clicked.connect(self.on_dialog_button_clicked) self.dialogPage.show() def on_dialog_button_clicked(self, button_name: str) -> None: - print(button_name) """Handle dialog button clicks""" if button_name == "Confirm": - self.ws.api.delete_file(self.filename,self.directory) + self.ws.api.delete_file(self.filename, self.directory) self.dialogPage.hide() else: self.dialogPage.hide() - def paintEvent(self, a0: QtGui.QPaintEvent) -> None: - """ - REFACTOR: Instead of using a background svg pixmap just draw the - background with with the correct styles and everything - """ + """Widget painting""" if self.babystepPage.isVisible(): _button_name_str = f"nozzle_offset_{self._z_offset}" if hasattr(self, _button_name_str): @@ -372,7 +343,7 @@ def setProperty(self, name: str, value: typing.Any) -> bool: if name == "backgroundPixmap": self.background = value return super().setProperty(name, value) - + def handle_cancel_print(self) -> None: """Handles the print cancel action""" self.ws.api.cancel_print() @@ -394,6 +365,7 @@ def back_button(self) -> None: self.request_back.emit() def setupMainPrintPage(self) -> None: + """Setup UI for print page""" self.setObjectName("printStackedWidget") self.setWindowModality(QtCore.Qt.WindowModality.WindowModal) self.resize(710, 410) @@ -409,9 +381,7 @@ def setupMainPrintPage(self) -> None: self.setMaximumSize(QtCore.QSize(720, 420)) self.setProperty( "backgroundPixmap", - QtGui.QPixmap( - ":/background/media/graphics/scroll_list_window.svg" - ), + QtGui.QPixmap(":/background/media/graphics/scroll_list_window.svg"), ) self.print_page = QtWidgets.QWidget() sizePolicy = QtWidgets.QSizePolicy( @@ -420,9 +390,7 @@ def setupMainPrintPage(self) -> None: ) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth( - self.print_page.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.print_page.sizePolicy().hasHeightForWidth()) self.print_page.setSizePolicy(sizePolicy) self.print_page.setMinimumSize(QtCore.QSize(710, 400)) self.print_page.setMaximumSize(QtCore.QSize(720, 420)) @@ -452,9 +420,7 @@ def setupMainPrintPage(self) -> None: self.main_print_btn.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.NoContextMenu ) - self.main_print_btn.setLayoutDirection( - QtCore.Qt.LayoutDirection.LeftToRight - ) + self.main_print_btn.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) self.main_print_btn.setStyleSheet("") self.main_print_btn.setAutoDefault(False) self.main_print_btn.setFlat(True) @@ -481,9 +447,7 @@ def setupMainPrintPage(self) -> None: font.setFamily("Montserrat") font.setPointSize(14) self.main_text_label.setFont(font) - self.main_text_label.setStyleSheet( - "background: transparent; color: white;" - ) + self.main_text_label.setStyleSheet("background: transparent; color: white;") self.main_text_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.main_text_label.setTextInteractionFlags( QtCore.Qt.TextInteractionFlag.NoTextInteraction @@ -497,6 +461,4 @@ def setupMainPrintPage(self) -> None: self.main_print_btn.setProperty( "class", _translate("printStackedWidget", "menu_btn") ) - self.main_text_label.setText( - _translate("printStackedWidget", "Printer ready") - ) + self.main_text_label.setText(_translate("printStackedWidget", "Printer ready")) diff --git a/BlocksScreen/lib/panels/userauthWindow.py b/BlocksScreen/lib/panels/userauthWindow.py deleted file mode 100644 index de1a7e0b..00000000 --- a/BlocksScreen/lib/panels/userauthWindow.py +++ /dev/null @@ -1,6 +0,0 @@ -import logging - - -# TODO: Create user authentication panel -# TODO: Create change user login -# TODO: Create admin mode diff --git a/BlocksScreen/lib/panels/utilitiesTab.py b/BlocksScreen/lib/panels/utilitiesTab.py index f67bc22d..4797b2ff 100644 --- a/BlocksScreen/lib/panels/utilitiesTab.py +++ b/BlocksScreen/lib/panels/utilitiesTab.py @@ -41,6 +41,8 @@ def get_gcode(self, name: str) -> str: class Process(Enum): + """Printer Process""" + FAN = auto() AXIS = auto() BED_HEATER = auto() @@ -119,7 +121,7 @@ def __init__( self.update_page = UpdatePage(self) self.addWidget(self.update_page) - + self.panel.utilities_input_shaper_btn.hide() # --- Back Buttons --- for button in ( @@ -234,6 +236,7 @@ def __init__( @QtCore.pyqtSlot(list, name="on_object_list") def on_object_list(self, object_list: list) -> None: + """Handle receiving printer object list""" self.cg = object_list for obj in self.cg: base_name = obj.split()[0] @@ -246,6 +249,7 @@ def on_object_list(self, object_list: list) -> None: @QtCore.pyqtSlot(dict, name="on_object_config") @QtCore.pyqtSlot(list, name="on_object_config") def on_object_config(self, config: typing.Union[dict, list]) -> None: + """Handle receiving printer object configurations""" if not config: return config_items = [config] if isinstance(config, dict) else config @@ -269,6 +273,7 @@ def on_object_config(self, config: typing.Union[dict, list]) -> None: } def on_printer_config_received(self, config: dict) -> None: + """Handle printer configuration""" for axis in ("x", "y", "z"): self.subscribe_config[str, "PyQt_PyObject"].emit( f"stepper_{axis}", self.on_object_config @@ -276,6 +281,7 @@ def on_printer_config_received(self, config: dict) -> None: @QtCore.pyqtSlot(str, list, name="on_gcode_move_update") def on_gcode_move_update(self, name: str, value: list) -> None: + """Handle gcode move""" if not value: return if name == "gcode_position": @@ -290,12 +296,14 @@ def _connect_numpad_request(self, button: QtWidgets.QWidget, name: str, title: s ) def handle_numpad_change(self, name: str, new_value: typing.Union[int, float]): + """Handle numpad change""" if name == "frequency": self.panel.isui_fq.setText(f"Frequency: {new_value} Hz") elif name == "smoothing": self.panel.isui_sm.setText(f"Smoothing: {new_value}") def run_routine(self, process: Process): + """Run check routine for available processes""" self.current_process = process routine_configs = { Process.FAN: ("fans", "fan is spinning"), @@ -325,8 +333,7 @@ def run_routine(self, process: Process): message = "Please check if the temperature reaches 60°C. \n you may need to wait a few moments." self.set_routine_check_page( - f"Running routine for: {self.current_object}", - message + f"Running routine for: {self.current_object}", message ) self.show_waiting_page( self.indexOf(self.panel.rc_page), @@ -358,6 +365,7 @@ def _advance_routine_object(self, obj_list: list) -> bool: return True def on_routine_answer(self) -> None: + """Handle routine ongoing process""" if self.current_process is None or self.current_object is None: return if self.sender() == self.panel.rc_yes: @@ -391,8 +399,10 @@ def _send_routine_gcode(self): if fan_name == "fan": self.run_gcode_signal.emit("M106 S255\nM400") else: - self.run_gcode_signal.emit(f"SET_FAN_SPEED FAN={fan_name} SPEED=0.8\nM400") - + self.run_gcode_signal.emit( + f"SET_FAN_SPEED FAN={fan_name} SPEED=0.8\nM400" + ) + return gcode_map = { @@ -412,18 +422,16 @@ def _send_routine_gcode(self): if gcode := gcode_map.get(key): self.run_gcode_signal.emit(f"{gcode}\nM400") - def set_routine_check_page(self, title: str, label: str): + """Set text on routine page""" self.panel.rc_tittle.setText(title) self.panel.rc_label.setText(label) def update_led_values(self) -> None: + """Update led state and color values""" if self.current_object not in self.objects["leds"]: return led_state: LedState = self.objects["leds"][self.current_object] - # led_state.red = self.panel.leds_r_slider.value() - # led_state.green = self.panel.leds_g_slider.value() - # led_state.blue = self.panel.leds_b_slider.value() led_state.white = int(self.panel.leds_w_slider.value() * 255 / 100) self.save_led_state() @@ -469,10 +477,12 @@ def _update_leds_from_config(self): partial(self.handle_led_button, led_names[0]) ) else: - self._connect_page_change(self.panel.utilities_leds_btn, self.panel.leds_page) - + self._connect_page_change( + self.panel.utilities_leds_btn, self.panel.leds_page + ) def toggle_led_state(self) -> None: + """Toggle leds""" if self.current_object not in self.objects["leds"]: return led_state: LedState = self.objects["leds"][self.current_object] @@ -485,30 +495,25 @@ def toggle_led_state(self) -> None: self.save_led_state() def handle_led_button(self, name: str) -> None: + """Handle led button clicked""" self.current_object = name led_state: LedState = self.objects["leds"].get(name) if not led_state: return is_rgb = led_state.led_type == "rgb" - # self.panel.leds_r_slider.setVisible(is_rgb) - # self.panel.leds_g_slider.setVisible(is_rgb) - # self.panel.leds_b_slider.setVisible(is_rgb) self.panel.leds_w_slider.setVisible(not is_rgb) - #self.panel.leds_slider_tittle_label.setText(name) - # self.panel.leds_r_slider.setValue(led_state.red) - # self.panel.leds_g_slider.setValue(led_state.green) - # self.panel.leds_b_slider.setValue(led_state.blue) self.panel.leds_w_slider.setValue(led_state.white) self.change_page(self.indexOf(self.panel.leds_slider_page)) def save_led_state(self): + """Save led state""" if self.current_object: if self.current_object in self.objects["leds"]: led_state: LedState = self.objects["leds"][self.current_object] self.run_gcode_signal.emit(led_state.get_gcode(self.current_object)) - # input shapper def run_resonance_test(self, axis: str) -> None: + """Perform Input Shaper Measure resonances test""" self.axis_in = axis path_map = { "x": "/tmp/resonances_x_axis_data.csv", @@ -530,7 +535,7 @@ def run_resonance_test(self, axis: str) -> None: self.x_inputshaper[panel_attr] = entry self.change_page(self.indexOf(self.panel.is_page)) - def _parse_shaper_csv(self, file_path: str) -> list: + def _parse_shaper_csv(self, file_path: str) -> list: results = [] try: with open(file_path, newline="") as csvfile: @@ -551,11 +556,12 @@ def _parse_shaper_csv(self, file_path: str) -> list: ) except FileNotFoundError: ... - except csv.Error as e: + except csv.Error: ... return results def apply_input_shaper_selection(self) -> None: + """Apply input shaper results""" if not (checked_button := self.panel.is_btn_group.checkedButton()): return selected_name = checked_button.objectName() @@ -575,6 +581,7 @@ def apply_input_shaper_selection(self) -> None: self.change_page(self.indexOf(self.panel.utilities_page)) def axis_maintenance(self, axis: str) -> None: + """Routine, checks axis movement for printer debugging""" self.current_process = Process.AXIS_MAINTENANCE self.current_object = axis self.run_gcode_signal.emit(f"G28 {axis.upper()}\nM400") @@ -605,10 +612,11 @@ def _run_axis_maintenance_gcode(self, axis: str): self.change_page(self.indexOf(self.panel.axes_page)) def troubleshoot_request(self) -> None: - self.troubleshoot_page.geometry_calc() + """Show troubleshoot page""" self.troubleshoot_page.show() def show_waiting_page(self, page_to_go_to: int, label: str, time_ms: int): + """Show placeholder page""" self.loadPage.label.setText(label) self.loadPage.show() QtCore.QTimer.singleShot(time_ms, lambda: self.change_page(page_to_go_to)) @@ -618,6 +626,7 @@ def _connect_page_change(self, button: QtWidgets.QWidget, page: QtWidgets.QWidge button.clicked.connect(lambda: self.change_page(self.indexOf(page))) def change_page(self, index: int): + """Request change page by index""" self.loadPage.hide() self.troubleshoot_page.hide() if index < self.count(): @@ -625,4 +634,5 @@ def change_page(self, index: int): @QtCore.pyqtSlot(name="request-back") def back_button(self) -> None: + """Request back""" self.request_back.emit() diff --git a/BlocksScreen/lib/panels/widgets/babystepPage.py b/BlocksScreen/lib/panels/widgets/babystepPage.py index 4df7403e..b0fdfdca 100644 --- a/BlocksScreen/lib/panels/widgets/babystepPage.py +++ b/BlocksScreen/lib/panels/widgets/babystepPage.py @@ -34,11 +34,13 @@ def __init__(self, parent) -> None: self.bbp_nozzle_offset_05.toggled.connect(self.handle_z_offset_change) self.bbp_nozzle_offset_1.toggled.connect(self.handle_z_offset_change) - self.savebutton.clicked.connect(self.savevalue) + self.savebutton.clicked.connect(self.save_value) @QtCore.pyqtSlot(name="on_move_nozzle_close") def on_move_nozzle_close(self) -> None: - """Move the nozzle closer to the print plate by the amount set in **` self._z_offset`**""" + """Move the nozzle closer to the print plate + by the amount set in **` self._z_offset`** + """ self.run_gcode.emit( f"SET_GCODE_OFFSET Z_ADJUST=-{self._z_offset}" # Z_ADJUST adds the value to the existing offset ) @@ -46,7 +48,9 @@ def on_move_nozzle_close(self) -> None: @QtCore.pyqtSlot(name="on_move_nozzle_away") def on_move_nozzle_away(self) -> None: - """Slot for Babystep button to get far from the bed by **` self._z_offset`** amount""" + """Slot for Babystep button to get far from the + bed by **` self._z_offset`** amount + """ self.run_gcode.emit( f"SET_GCODE_OFFSET Z_ADJUST=+{self._z_offset}" # Z_ADJUST adds the value to the existing offset ) @@ -69,30 +73,25 @@ def handle_z_offset_change(self) -> None: return self._z_offset = float(_sender.text()[:-3]) - def savevalue(self): + def save_value(self): + """Save new z offset value""" self.run_gcode.emit("Z_OFFSET_APPLY_PROBE") self.savebutton.setVisible(False) - self.bbp_z_offset_title_label.setText( - self.bbp_z_offset_current_value.text() - ) - - return + self.bbp_z_offset_title_label.setText(self.bbp_z_offset_current_value.text()) def on_gcode_move_update(self, name: str, value: list) -> None: + """Handle gcode move updates""" if not value: return if name == "homing_origin": self._z_offset_text = value[2] - self.bbp_z_offset_current_value.setText( - f"Z: {self._z_offset_text:.3f}mm" - ) + self.bbp_z_offset_current_value.setText(f"Z: {self._z_offset_text:.3f}mm") if self.bbp_z_offset_title_label.text() == "smth": - self.bbp_z_offset_title_label.setText( - f"Z: {self._z_offset_text:.3f}mm" - ) + self.bbp_z_offset_title_label.setText(f"Z: {self._z_offset_text:.3f}mm") def setupUI(self): + """Setup babystep page ui""" self.bbp_offset_value_selector_group = QtWidgets.QButtonGroup(self) self.bbp_offset_value_selector_group.setExclusive(True) sizePolicy = QtWidgets.QSizePolicy( @@ -148,9 +147,7 @@ def setupUI(self): self.savebutton.setGeometry(QtCore.QRect(460, 340, 200, 60)) self.savebutton.setText("Save?") self.savebutton.setObjectName("savebutton") - self.savebutton.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/save.svg") - ) + self.savebutton.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/save.svg")) self.savebutton.setVisible(False) font = QtGui.QFont() font.setPointSize(15) @@ -178,16 +175,13 @@ def setupUI(self): self.babystep_back_btn.setMaximumSize(QtCore.QSize(60, 60)) self.babystep_back_btn.setText("") self.babystep_back_btn.setFlat(True) - self.babystep_back_btn.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/back.svg") - ) + self.babystep_back_btn.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back.svg")) self.babystep_back_btn.setObjectName("babystep_back_btn") self.bbp_header_layout.addWidget( self.babystep_back_btn, 0, - QtCore.Qt.AlignmentFlag.AlignRight - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter, ) self.bbp_header_layout.setStretch(0, 1) self.verticalLayout.addLayout(self.bbp_header_layout) @@ -233,14 +227,11 @@ def setupUI(self): self.bbp_nozzle_offset_1.setFlat(True) self.bbp_nozzle_offset_1.setProperty("button_type", "") self.bbp_nozzle_offset_1.setObjectName("bbp_nozzle_offset_1") - self.bbp_offset_value_selector_group.addButton( - self.bbp_nozzle_offset_1 - ) + self.bbp_offset_value_selector_group.addButton(self.bbp_nozzle_offset_1) self.bbp_offset_steps_buttons.addWidget( self.bbp_nozzle_offset_1, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # Line separator for 0.1mm - set size policy to expanding horizontally @@ -262,14 +253,11 @@ def setupUI(self): self.bbp_nozzle_offset_01.setFlat(True) self.bbp_nozzle_offset_01.setProperty("button_type", "") self.bbp_nozzle_offset_01.setObjectName("bbp_nozzle_offset_01") - self.bbp_offset_value_selector_group.addButton( - self.bbp_nozzle_offset_01 - ) + self.bbp_offset_value_selector_group.addButton(self.bbp_nozzle_offset_01) self.bbp_offset_steps_buttons.addWidget( self.bbp_nozzle_offset_01, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # 0.05mm button @@ -289,14 +277,11 @@ def setupUI(self): self.bbp_nozzle_offset_05.setFlat(True) self.bbp_nozzle_offset_05.setProperty("button_type", "") self.bbp_nozzle_offset_05.setObjectName("bbp_nozzle_offset_05") - self.bbp_offset_value_selector_group.addButton( - self.bbp_nozzle_offset_05 - ) + self.bbp_offset_value_selector_group.addButton(self.bbp_nozzle_offset_05) self.bbp_offset_steps_buttons.addWidget( self.bbp_nozzle_offset_05, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # 0.025mm button @@ -316,22 +301,17 @@ def setupUI(self): self.bbp_nozzle_offset_025.setFlat(True) self.bbp_nozzle_offset_025.setProperty("button_type", "") self.bbp_nozzle_offset_025.setObjectName("bbp_nozzle_offset_025") - self.bbp_offset_value_selector_group.addButton( - self.bbp_nozzle_offset_025 - ) + self.bbp_offset_value_selector_group.addButton(self.bbp_nozzle_offset_025) self.bbp_offset_steps_buttons.addWidget( self.bbp_nozzle_offset_025, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # Line separator for 0.025mm - set size policy to expanding horizontally # Set the layout for the group box - self.bbp_offset_steps_buttons_group_box.setLayout( - self.bbp_offset_steps_buttons - ) + self.bbp_offset_steps_buttons_group_box.setLayout(self.bbp_offset_steps_buttons) # Add the group box to the main content horizontal layout FIRST for left placement self.main_content_horizontal_layout.addWidget( self.bbp_offset_steps_buttons_group_box @@ -339,9 +319,7 @@ def setupUI(self): # Graphic and Current Value Frame (This will now be in the MIDDLE) self.frame_2 = QtWidgets.QFrame(parent=self) - sizePolicy.setHeightForWidth( - self.frame_2.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) self.frame_2.setSizePolicy(sizePolicy) self.frame_2.setMinimumSize(QtCore.QSize(350, 160)) self.frame_2.setMaximumSize(QtCore.QSize(350, 160)) @@ -357,18 +335,14 @@ def setupUI(self): QtGui.QPixmap(":/graphics/media/graphics/babystep_graphic.png") ) self.bbp_babystep_graphic.setScaledContents(False) - self.bbp_babystep_graphic.setAlignment( - QtCore.Qt.AlignmentFlag.AlignCenter - ) + self.bbp_babystep_graphic.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.bbp_babystep_graphic.setObjectName("bbp_babystep_graphic") # === NEW LABEL ADDED HERE === # This is the title label that appears above the red value box. self.bbp_z_offset_title_label = QtWidgets.QLabel(parent=self) # Position it just above the red box. Red box is at y=70, so y=40 is appropriate. - self.bbp_z_offset_title_label.setGeometry( - QtCore.QRect(100, 40, 200, 30) - ) + self.bbp_z_offset_title_label.setGeometry(QtCore.QRect(100, 40, 200, 30)) font = QtGui.QFont() font.setPointSize(12) @@ -385,9 +359,7 @@ def setupUI(self): # === END OF NEW LABEL === self.bbp_z_offset_current_value = BlocksLabel(parent=self.frame_2) - self.bbp_z_offset_current_value.setGeometry( - QtCore.QRect(100, 70, 200, 60) - ) + self.bbp_z_offset_current_value.setGeometry(QtCore.QRect(100, 70, 200, 60)) sizePolicy.setHeightForWidth( self.bbp_z_offset_current_value.sizePolicy().hasHeightForWidth() ) @@ -407,15 +379,12 @@ def setupUI(self): self.bbp_z_offset_current_value.setAlignment( QtCore.Qt.AlignmentFlag.AlignCenter ) - self.bbp_z_offset_current_value.setObjectName( - "bbp_z_offset_current_value" - ) + self.bbp_z_offset_current_value.setObjectName("bbp_z_offset_current_value") # Add graphic frame AFTER the offset buttons group box self.main_content_horizontal_layout.addWidget( self.frame_2, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # Move Buttons Layout (This will now be on the RIGHT) @@ -423,9 +392,7 @@ def setupUI(self): self.bbp_buttons_layout.setContentsMargins(5, 5, 5, 5) self.bbp_buttons_layout.setObjectName("bbp_buttons_layout") self.bbp_mvup = IconButton(parent=self) - sizePolicy.setHeightForWidth( - self.bbp_mvup.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.bbp_mvup.sizePolicy().hasHeightForWidth()) self.bbp_mvup.setSizePolicy(sizePolicy) self.bbp_mvup.setMinimumSize(QtCore.QSize(80, 80)) self.bbp_mvup.setMaximumSize(QtCore.QSize(80, 80)) @@ -442,9 +409,7 @@ def setupUI(self): self.bbp_mvup, 0, QtCore.Qt.AlignmentFlag.AlignRight ) self.bbp_mvdown = IconButton(parent=self) - sizePolicy.setHeightForWidth( - self.bbp_mvdown.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.bbp_mvdown.sizePolicy().hasHeightForWidth()) self.bbp_mvdown.setSizePolicy(sizePolicy) self.bbp_mvdown.setMinimumSize(QtCore.QSize(80, 80)) self.bbp_mvdown.setMaximumSize(QtCore.QSize(80, 80)) diff --git a/BlocksScreen/lib/panels/widgets/confirmPage.py b/BlocksScreen/lib/panels/widgets/confirmPage.py index 525b0bbe..95a12579 100644 --- a/BlocksScreen/lib/panels/widgets/confirmPage.py +++ b/BlocksScreen/lib/panels/widgets/confirmPage.py @@ -43,6 +43,7 @@ def __init__(self, parent) -> None: @QtCore.pyqtSlot(str, dict, name="on_show_widget") def on_show_widget(self, text: str, filedata: dict | None = None) -> None: + """Handle widget show""" directory = os.path.dirname(text) filename = os.path.basename(text) self.directory = directory @@ -101,11 +102,13 @@ def estimate_print_time(self, seconds: int) -> list: return [days, hours, minutes, seconds] def hide(self): + """Hide widget""" self.directory = "" self.filename = "" return super().hide() def paintEvent(self, event: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" if not self.isVisible(): self.directory = "" self.filename = "" @@ -144,14 +147,13 @@ def paintEvent(self, event: QtGui.QPaintEvent) -> None: self._scene.setSceneRect(graphics_rect) def showEvent(self, a0: QtGui.QShowEvent) -> None: + """Re-implemented method, Handle widget show event""" if not self.thumbnail: self.cf_thumbnail.close() return super().showEvent(a0) - def hideEvent(self, a0: QtGui.QHideEvent) -> None: - return super().hideEvent(a0) - def setupUI(self) -> None: + """Setup widget ui""" sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding, diff --git a/BlocksScreen/lib/panels/widgets/connectionPage.py b/BlocksScreen/lib/panels/widgets/connectionPage.py index 7760cca5..33bd0576 100644 --- a/BlocksScreen/lib/panels/widgets/connectionPage.py +++ b/BlocksScreen/lib/panels/widgets/connectionPage.py @@ -3,20 +3,16 @@ from events import KlippyDisconnected, KlippyReady, KlippyShutdown from lib.moonrakerComm import MoonWebSocket from lib.ui.connectionWindow_ui import Ui_ConnectivityForm -from PyQt6 import QtCore, QtWidgets, QtGui +from PyQt6 import QtCore, QtWidgets class ConnectionPage(QtWidgets.QFrame): text_updated = QtCore.pyqtSignal(int, name="connection_text_updated") - retry_connection_clicked = QtCore.pyqtSignal( - name="retry_connection_clicked" - ) + retry_connection_clicked = QtCore.pyqtSignal(name="retry_connection_clicked") wifi_button_clicked = QtCore.pyqtSignal(name="call_network_page") reboot_clicked = QtCore.pyqtSignal(name="reboot_clicked") restart_klipper_clicked = QtCore.pyqtSignal(name="restart_klipper_clicked") - firmware_restart_clicked = QtCore.pyqtSignal( - name="firmware_restart_clicked" - ) + firmware_restart_clicked = QtCore.pyqtSignal(name="firmware_restart_clicked") def __init__(self, parent: QtWidgets.QWidget, ws: MoonWebSocket, /): super().__init__(parent) @@ -31,7 +27,7 @@ def __init__(self, parent: QtWidgets.QWidget, ws: MoonWebSocket, /): self.message = None self.dot_timer = QtCore.QTimer(self) self.dot_timer.setInterval(1000) - self.dot_timer.timeout.connect(self.add_dot) + self.dot_timer.timeout.connect(self._add_dot) self.installEventFilter(self.parent()) @@ -51,6 +47,7 @@ def __init__(self, parent: QtWidgets.QWidget, ws: MoonWebSocket, /): self.ws.klippy_state_signal.connect(self.on_klippy_state) def show_panel(self, reason: str | None = None): + """Show widget""" self.show() if reason is not None: self.text_update(reason) @@ -58,12 +55,9 @@ def show_panel(self, reason: str | None = None): self.text_update() return False - @QtCore.pyqtSlot(bool, name="klippy_connection") - def on_klippy_connection(self, state: bool): - pass - @QtCore.pyqtSlot(str, name="on_klippy_state") def on_klippy_state(self, state: str): + """Handle klippy state changes""" if state == "error": self.panel.connectionTextBox.setText("Klipper Connection Error") if not self.isVisible(): @@ -87,22 +81,24 @@ def on_klippy_state(self, state: str): @QtCore.pyqtSlot(int, name="on_websocket_connecting") @QtCore.pyqtSlot(str, name="on_websocket_connecting") def on_websocket_connecting(self, attempt: int): + """Handle websocket connecting state""" self.text_update(attempt) @QtCore.pyqtSlot(name="on_websocket_connection_achieved") def on_websocket_connection_achieved(self): - self.panel.connectionTextBox.setText( - "Moonraker Connected\n Klippy not ready" - ) + """Handle websocket connected state""" + self.panel.connectionTextBox.setText("Moonraker Connected\n Klippy not ready") self.hide() @QtCore.pyqtSlot(name="on_websocket_connection_lost") def on_websocket_connection_lost(self): + """Handle websocket connection lost state""" if not self.isVisible(): self.show() self.text_update(text="Websocket lost") def text_update(self, text: int | str | None = None): + """Update widget text""" if self.state == "shutdown" and self.message is not None: return False @@ -142,7 +138,7 @@ def text_update(self, text: int | str | None = None): return False - def add_dot(self): + def _add_dot(self): if self.state == "shutdown" and self.message is not None: self.dot_timer.stop() return False @@ -156,13 +152,13 @@ def add_dot(self): @QtCore.pyqtSlot(str, str, name="webhooks_update") def webhook_update(self, state: str, message: str): + """Handle websocket webhook updates""" self.state = state self.message = message self.text_update() - def eventFilter( - self, object: QtCore.QObject, event: QtCore.QEvent - ) -> bool: + def eventFilter(self, object: QtCore.QObject, event: QtCore.QEvent) -> bool: + """Re-implemented method, filter events""" if event.type() == KlippyDisconnected.type(): if not self.isVisible(): self.panel.connectionTextBox.setText("Klippy Disconnected") diff --git a/BlocksScreen/lib/panels/widgets/dialogPage.py b/BlocksScreen/lib/panels/widgets/dialogPage.py index 7ed3d42c..65f7c727 100644 --- a/BlocksScreen/lib/panels/widgets/dialogPage.py +++ b/BlocksScreen/lib/panels/widgets/dialogPage.py @@ -2,31 +2,28 @@ class DialogPage(QtWidgets.QDialog): - button_clicked = QtCore.pyqtSignal( - str - ) # Signal to emit which button was clicked + button_clicked = QtCore.pyqtSignal(str) # Signal to emit which button was clicked def __init__( self, parent: QtWidgets.QWidget, ) -> None: super().__init__(parent) - self.setWindowFlags( - QtCore.Qt.WindowType.Popup - | QtCore.Qt.WindowType.FramelessWindowHint + QtCore.Qt.WindowType.Popup | QtCore.Qt.WindowType.FramelessWindowHint ) self.setAttribute( QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True ) # Make background transparent - - self.setupUI() + self._setupUI() self.repaint() def set_message(self, message: str) -> None: + """Set dialog text message""" self.label.setText(message) - def geometry_calc(self) -> None: + def _geometry_calc(self) -> None: + """Calculate dialog widget position relative to the window""" app_instance = QtWidgets.QApplication.instance() main_window = app_instance.activeWindow() if app_instance else None if main_window is None and app_instance: @@ -42,14 +39,13 @@ def geometry_calc(self) -> None: self.testwidth = width self.testheight = height x = int(main_window.geometry().x() + (main_window.width() - width) / 2) - y = int( - main_window.geometry().y() + (main_window.height() - height) / 2 - ) + y = int(main_window.geometry().y() + (main_window.height() - height) / 2) self.setGeometry(x, y, width, height) def paintEvent(self, event: QtGui.QPaintEvent) -> None: - self.geometry_calc() + """Re-implemented method, paint widget""" + self._geometry_calc() painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True) @@ -74,10 +70,10 @@ def paintEvent(self, event: QtGui.QPaintEvent) -> None: painter.end() def sizeHint(self) -> QtCore.QSize: + """Re-implemented method, widget size hint""" popup_width = int(self.geometry().width()) popup_height = int(self.geometry().height()) # Centering logic - popup_x = self.x() popup_y = self.y() + (self.height() - popup_height) // 2 self.move(popup_x, popup_y) @@ -85,10 +81,8 @@ def sizeHint(self) -> QtCore.QSize: self.setMinimumSize(popup_width, popup_height) return super().sizeHint() - def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None: - return - def resizeEvent(self, event: QtGui.QResizeEvent) -> None: + """Re-implemented method, handle resize event""" super().resizeEvent(event) label_width = self.testwidth @@ -112,10 +106,11 @@ def resizeEvent(self, event: QtGui.QResizeEvent) -> None: ) def show(self) -> None: - self.geometry_calc() + """Re-implemented method, show widget""" + self._geometry_calc() return super().show() - def setupUI(self) -> None: + def _setupUI(self) -> None: self.label = QtWidgets.QLabel("Test", self) font = QtGui.QFont() font.setPointSize(25) @@ -166,17 +161,12 @@ def setupUI(self) -> None: ) # Connect button signals - self.confirm_button.clicked.connect( - lambda: self.on_button_clicked("Confirm") - ) - self.cancel_button.clicked.connect( - lambda: self.on_button_clicked("Cancel") - ) + self.confirm_button.clicked.connect(lambda: self.on_button_clicked("Confirm")) + self.cancel_button.clicked.connect(lambda: self.on_button_clicked("Cancel")) def on_button_clicked(self, button_name: str) -> None: - self.button_clicked.emit( - button_name - ) # Emit the signal with the button name + """Handle dialog buttons clicked""" + self.button_clicked.emit(button_name) # Emit the signal with the button name if button_name == "Confirm": self.accept() # Close the dialog with an accepted state elif button_name == "Cancel": diff --git a/BlocksScreen/lib/panels/widgets/fansPage.py b/BlocksScreen/lib/panels/widgets/fansPage.py index 5dffba42..925c0230 100644 --- a/BlocksScreen/lib/panels/widgets/fansPage.py +++ b/BlocksScreen/lib/panels/widgets/fansPage.py @@ -1,13 +1,15 @@ from PyQt6 import QtCore, QtWidgets -import typing +import typing -class FansPage(QtWidgets.QWidget): +class FansPage(QtWidgets.QWidget): def __init__( - self, parent: typing.Optional["QtWidgets.QWidget"], flags: typing.Optional["QtCore.Qt.WindowType"] + self, + parent: typing.Optional["QtWidgets.QWidget"], + flags: typing.Optional["QtCore.Qt.WindowType"], ) -> None: - if parent is not None and flags is not None: + if parent is not None and flags is not None: super(FansPage, self).__init__(parent, flags) - else : - super(FansPage, self).__init__() \ No newline at end of file + else: + super(FansPage, self).__init__() diff --git a/BlocksScreen/lib/panels/widgets/filesPage.py b/BlocksScreen/lib/panels/widgets/filesPage.py index 238c5eed..8de160c0 100644 --- a/BlocksScreen/lib/panels/widgets/filesPage.py +++ b/BlocksScreen/lib/panels/widgets/filesPage.py @@ -27,8 +27,8 @@ class FilesPage(QtWidgets.QWidget): request_file_list: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( [], [str], name="api-get-files-list" ) - request_file_metadata: typing.ClassVar[QtCore.pyqtSignal] = ( - QtCore.pyqtSignal(str, name="api-get-gcode-metadata") + request_file_metadata: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + str, name="api-get-gcode-metadata" ) file_list: list = [] files_data: dict = {} @@ -43,9 +43,7 @@ def __init__(self, parent) -> None: self.ReloadButton.clicked.connect( lambda: self.request_dir_info[str].emit(self.curr_dir) ) - self.listWidget.verticalScrollBar().valueChanged.connect( - self._handle_scrollbar - ) + self.listWidget.verticalScrollBar().valueChanged.connect(self._handle_scrollbar) self.scrollbar.valueChanged.connect(self._handle_scrollbar) self.scrollbar.valueChanged.connect( lambda value: self.listWidget.verticalScrollBar().setValue(value) @@ -54,31 +52,36 @@ def __init__(self, parent) -> None: @QtCore.pyqtSlot(name="reset-dir") def reset_dir(self) -> None: + """Reset current directory""" self.curr_dir = "" self.request_dir_info[str].emit(self.curr_dir) def showEvent(self, a0: QtGui.QShowEvent) -> None: + """Re-implemented method, handle widget show event""" self._build_file_list() return super().showEvent(a0) @QtCore.pyqtSlot(list, name="on-file-list") def on_file_list(self, file_list: list) -> None: + """Handle receiving files list from websocket""" self.files_data.clear() self.file_list = file_list - # if self.isVisible(): # Only build the list when directories come - # self._build_file_list() @QtCore.pyqtSlot(list, name="on-dirs") def on_directories(self, directories_data: list) -> None: + """Handle receiving available directories from websocket""" self.directories = directories_data if self.isVisible(): self._build_file_list() @QtCore.pyqtSlot(str, name="on-delete-file") - def on_delete_file(self, filename: str) -> None: ... + def on_delete_file(self, filename: str) -> None: + """Handle file deleted""" + ... @QtCore.pyqtSlot(dict, name="on-fileinfo") def on_fileinfo(self, filedata: dict) -> None: + """Handle receive file information/metadata""" if not filedata or not self.isVisible(): return filename = filedata.get("filename", "") @@ -86,11 +89,7 @@ def on_fileinfo(self, filedata: dict) -> None: return self.files_data.update({f"{filename}": filedata}) estimated_time = filedata.get("estimated_time", 0) - seconds = ( - int(estimated_time) - if isinstance(estimated_time, (int, float)) - else 0 - ) + seconds = int(estimated_time) if isinstance(estimated_time, (int, float)) else 0 filament_type = ( filedata.get("filament_type", "Unknown filament") if filedata.get("filament_type", "Unknown filament") != -1.0 @@ -110,9 +109,7 @@ def on_fileinfo(self, filedata: dict) -> None: else: time_str = f"{minutes}m" - list_items = [ - self.listWidget.item(i) for i in range(self.listWidget.count()) - ] + list_items = [self.listWidget.item(i) for i in range(self.listWidget.count())] if not list_items: return for list_item in list_items: @@ -131,17 +128,13 @@ def _fileItemClicked(self, item: QtWidgets.QListWidgetItem) -> None: widget = self.listWidget.itemWidget(item) for file in self.file_list: path = ( - file.get("path") - if "path" in file.keys() - else file.get("filename") + file.get("path") if "path" in file.keys() else file.get("filename") ) if not path: return if widget.text() in path: file_path = ( - path - if not self.curr_dir - else str(self.curr_dir + "/" + path) + path if not self.curr_dir else str(self.curr_dir + "/" + path) ) self.file_selected.emit( str(file_path.removeprefix("/")), @@ -151,9 +144,7 @@ def _fileItemClicked(self, item: QtWidgets.QListWidgetItem) -> None: ) @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, str, name="dir-item-clicked") - def _dirItemClicked( - self, item: QtWidgets.QListWidgetItem, directory: str - ) -> None: + def _dirItemClicked(self, item: QtWidgets.QListWidgetItem, directory: str) -> None: self.curr_dir = self.curr_dir + directory self.request_dir_info[str].emit(self.curr_dir) @@ -172,9 +163,7 @@ def _build_file_list(self) -> None: if dir_data.get("dirname").startswith("."): continue self._add_directory_list_item(dir_data) - sorted_list = sorted( - self.file_list, key=lambda x: x["modified"], reverse=True - ) + sorted_list = sorted(self.file_list, key=lambda x: x["modified"], reverse=True) for item in sorted_list: self._add_file_list_item(item) self._add_spacer() @@ -188,9 +177,7 @@ def _add_directory_list_item(self, dir_data: dict) -> None: return button = ListCustomButton() button.setText(str(dir_data.get("dirname"))) - button.setSecondPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/folderIcon.svg") - ) + button.setSecondPixmap(QtGui.QPixmap(":/ui/media/btn_icons/folderIcon.svg")) button.setMinimumSize(600, 80) button.setMaximumSize(700, 80) button.setLeftFontSize(17) @@ -199,16 +186,12 @@ def _add_directory_list_item(self, dir_data: dict) -> None: list_item.setSizeHint(button.sizeHint()) self.listWidget.addItem(list_item) self.listWidget.setItemWidget(list_item, button) - button.clicked.connect( - lambda: self._dirItemClicked(list_item, "/" + dir_name) - ) + button.clicked.connect(lambda: self._dirItemClicked(list_item, "/" + dir_name)) def _add_back_folder_entry(self) -> None: button = ListCustomButton() button.setText("Go Back") - button.setSecondPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/back_folder.svg") - ) + button.setSecondPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back_folder.svg")) button.setMinimumSize(600, 80) button.setMaximumSize(700, 80) button.setLeftFontSize(17) @@ -241,9 +224,7 @@ def _add_file_list_item(self, file_data_item) -> None: return button = ListCustomButton() button.setText(name[:-6]) - button.setPixmap( - QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg") - ) + button.setPixmap(QtGui.QPixmap(":/arrow_icons/media/btn_icons/right_arrow.svg")) button.setMinimumSize(600, 80) button.setMaximumSize(700, 80) button.setLeftFontSize(17) @@ -274,8 +255,7 @@ def _add_placeholder(self) -> None: placeholder_label.setFont(font) placeholder_label.setStyleSheet("color: gray;") placeholder_label.setAlignment( - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter ) placeholder_label.setMinimumSize( QtCore.QSize(self.listWidget.width(), self.listWidget.height()) @@ -295,15 +275,9 @@ def _handle_scrollbar(self, value): self.scrollbar.blockSignals(False) def _setup_scrollbar(self) -> None: - self.scrollbar.setMinimum( - self.listWidget.verticalScrollBar().minimum() - ) - self.scrollbar.setMaximum( - self.listWidget.verticalScrollBar().maximum() - ) - self.scrollbar.setPageStep( - self.listWidget.verticalScrollBar().pageStep() - ) + self.scrollbar.setMinimum(self.listWidget.verticalScrollBar().minimum()) + self.scrollbar.setMaximum(self.listWidget.verticalScrollBar().maximum()) + self.scrollbar.setPageStep(self.listWidget.verticalScrollBar().pageStep()) self.scrollbar.show() def _setupUI(self): @@ -321,9 +295,7 @@ def _setupUI(self): self.setFont(font) self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) self.setAutoFillBackground(False) - self.setStyleSheet( - "#file_page{\n background-color: transparent;\n}" - ) + self.setStyleSheet("#file_page{\n background-color: transparent;\n}") self.verticalLayout_5 = QtWidgets.QVBoxLayout(self) self.verticalLayout_5.setObjectName("verticalLayout_5") self.fp_header_layout = QtWidgets.QHBoxLayout() diff --git a/BlocksScreen/lib/panels/widgets/jobStatusPage.py b/BlocksScreen/lib/panels/widgets/jobStatusPage.py index e632dc2f..bb9754ea 100644 --- a/BlocksScreen/lib/panels/widgets/jobStatusPage.py +++ b/BlocksScreen/lib/panels/widgets/jobStatusPage.py @@ -12,12 +12,16 @@ class ClickableGraphicsView(QtWidgets.QGraphicsView): + """Re-implementation of QGraphicsView that adds clicked signal""" + clicked = QtCore.pyqtSignal() def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: + """Filter mouse press events""" if event.button() == QtCore.Qt.MouseButton.LeftButton: self.clicked.emit() + return True # Issue event handled super(ClickableGraphicsView, self).mousePressEvent(event) @@ -59,7 +63,7 @@ def __init__(self, parent) -> None: super().__init__(parent) self.canceldialog = dialogPage.DialogPage(self) - self.setupUI() + self._setupUI() self.tune_menu_btn.clicked.connect(self.tune_clicked.emit) self.pause_printing_btn.clicked.connect(self.pause_resume_print) self.stop_printing_btn.clicked.connect(self.handleCancel) @@ -77,6 +81,7 @@ def __init__(self, parent) -> None: self.CBVBigThumbnail.installEventFilter(self) def eventFilter(self, source, event): + """Re-implemented method, filter events""" if ( source == self.CBVSmallThumbnail and event.type() == QtCore.QEvent.Type.MouseButtonPress @@ -95,6 +100,7 @@ def eventFilter(self, source, event): @QtCore.pyqtSlot(name="show-thumbnail") def showthumbnail(self): + """Show print job fullscreen thumbnail""" self.contentWidget.hide() self.progressWidget.hide() self.headerWidget.hide() @@ -104,6 +110,7 @@ def showthumbnail(self): @QtCore.pyqtSlot(name="hide-thumbnail") def hidethumbnail(self): + """Hide print job fullscreen thumbnail""" self.contentWidget.show() self.progressWidget.show() self.headerWidget.show() @@ -113,7 +120,7 @@ def hidethumbnail(self): @QtCore.pyqtSlot(name="handle-cancel") def handleCancel(self) -> None: - """Handle the cancel print job dialog""" + """Handle cancel print job dialog""" self.canceldialog.set_message( "Are you sure you \n want to cancel \n this print job?" ) @@ -159,6 +166,7 @@ def on_print_start(self, file: str, thumbnails: list) -> None: @QtCore.pyqtSlot(dict, name="on_fileinfo") def on_fileinfo(self, fileinfo: dict) -> None: + """Handle received file information/metadata""" self.total_layers = str(fileinfo.get("layer_count", "?")) self.layer_display_button.setText("?") if ( @@ -175,6 +183,7 @@ def on_fileinfo(self, fileinfo: dict) -> None: @QtCore.pyqtSlot(name="pause_resume_print") def pause_resume_print(self) -> None: + """Handle pause/resume print job""" if not getattr(self, "_pause_locked", False): self._pause_locked = True self.pause_printing_btn.setEnabled(False) @@ -233,7 +242,6 @@ def on_print_stats_update(self, field: str, value: dict | float | str) -> None: self.file_metadata.clear() self.hide_request.emit() - if hasattr(events, str("Print" + value.capitalize())): event_obj = getattr(events, str("Print" + value.capitalize())) event = event_obj(self._current_file_name, self.file_metadata) @@ -278,13 +286,7 @@ def on_print_stats_update(self, field: str, value: dict | float | str) -> None: @QtCore.pyqtSlot(str, list, name="on_gcode_move_update") def on_gcode_move_update(self, field: str, value: list) -> None: - # """Processes the information that comes from the printer object "gcode_move" - - # Args: - # field (str): Name of the updated field - # value (list): New value for the field - # """ - + """Handle gcode move""" if isinstance(value, list): if "gcode_position" in field: # Without offsets if self._internal_print_status == "printing": @@ -307,7 +309,7 @@ def on_gcode_move_update(self, field: str, value: list) -> None: @QtCore.pyqtSlot(str, float, name="virtual_sdcard_update") @QtCore.pyqtSlot(str, bool, name="virtual_sdcard_update") def virtual_sdcard_update(self, field: str, value: float | bool) -> None: - """Slot for incoming printer object virtual_sdcard information update + """Handle virtual sdcard Args: field (str): Name of the updated field on the virtual_sdcard object @@ -321,6 +323,7 @@ def virtual_sdcard_update(self, field: str, value: float | bool) -> None: self.printing_progress_bar.setValue(self.print_progress) def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" _scene = QtWidgets.QGraphicsScene() if not self.smalthumbnail.isNull(): _graphics_rect = self.CBVSmallThumbnail.rect().toRectF() @@ -380,7 +383,8 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: _scene.addItem(_item_scaled) self.CBVBigThumbnail.setScene(_scene) - def setupUI(self) -> None: + def _setupUI(self) -> None: + """Setup widget ui""" sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding, diff --git a/BlocksScreen/lib/panels/widgets/keyboardPage.py b/BlocksScreen/lib/panels/widgets/keyboardPage.py index 07c9b338..4f1d13d8 100644 --- a/BlocksScreen/lib/panels/widgets/keyboardPage.py +++ b/BlocksScreen/lib/panels/widgets/keyboardPage.py @@ -4,7 +4,7 @@ class CustomQwertyKeyboard(QtWidgets.QWidget): - """A custom numpad for inserting integer values""" + """A custom keyboard for inserting integer values""" value_selected = QtCore.pyqtSignal(str, name="value_selected") request_back = QtCore.pyqtSignal(name="request_back") @@ -14,7 +14,7 @@ def __init__( parent, ) -> None: super().__init__(parent) - self.setupUi() + self._setupUi() self.current_value: str = "" self.symbolsrun = False self.setCursor( @@ -54,8 +54,8 @@ def __init__( self.inserted_value.setText("") - self.K_keychange.clicked.connect(self.handle_checkbuttons) - self.K_shift.clicked.connect(self.handle_checkbuttons) + self.K_keychange.clicked.connect(self.handle_keyboard_layout) + self.K_shift.clicked.connect(self.handle_keyboard_layout) self.numpad_back_btn.clicked.connect(lambda: self.request_back.emit()) @@ -75,9 +75,10 @@ def __init__( color: white; } """) - self.handle_checkbuttons() + self.handle_keyboard_layout() - def handle_checkbuttons(self): + def handle_keyboard_layout(self): + """Verifies if shift is toggled, changes layout accordingly""" shift = self.K_shift.isChecked() keychange = self.K_keychange.isChecked() @@ -207,7 +208,7 @@ def handle_checkbuttons(self): self.K_shift.setText("Shift") def value_inserted(self, value: str) -> None: - """Handle number insertion on the numpad + """Handle value insertion on the keyboard Args: value (int | str): value @@ -235,10 +236,11 @@ def value_inserted(self, value: str) -> None: self.inserted_value.setText(str(self.current_value)) def set_value(self, value: str) -> None: + """Set keyboard value""" self.current_value = value self.inserted_value.setText(value) - def setupUi(self): + def _setupUi(self): self.setObjectName("self") self.setEnabled(True) self.resize(800, 480) diff --git a/BlocksScreen/lib/panels/widgets/loadPage.py b/BlocksScreen/lib/panels/widgets/loadPage.py index d4ddcbcf..f5fd7963 100644 --- a/BlocksScreen/lib/panels/widgets/loadPage.py +++ b/BlocksScreen/lib/panels/widgets/loadPage.py @@ -5,9 +5,8 @@ class LoadScreen(QtWidgets.QDialog): class AnimationGIF(enum.Enum): - # [x]: WATHERE ARE NO GIFS IN LOADSCREEN PLEASE REMEMBER THIS IM WARNING + """Animation type""" - # TODO : add more types into LoadScreen DEFAULT = None PLACEHOLDER = "" @@ -31,10 +30,9 @@ def __init__( ) self.setWindowFlags( - QtCore.Qt.WindowType.Popup - | QtCore.Qt.WindowType.FramelessWindowHint + QtCore.Qt.WindowType.Popup | QtCore.Qt.WindowType.FramelessWindowHint ) - self.setupUI() + self._setupUI() config: BlocksScreenConfig = get_configparser() try: if config: @@ -61,10 +59,11 @@ def __init__( self.repaint() def set_status_message(self, message: str) -> None: + """Set widget status message""" self.label.setText(message) - def geometry_calc(self) -> None: - # REFACTOR: find another way to get mainwindow geometry , this version consumes too much ram + def _geometry_calc(self) -> None: + """Calculate widget position relative to the screen""" app_instance = QtWidgets.QApplication.instance() main_window = app_instance.activeWindow() if app_instance else None if main_window is None and app_instance: @@ -79,6 +78,7 @@ def geometry_calc(self) -> None: self.setGeometry(x, y, width, height) def close(self) -> bool: + """Re-implemented method, close widget""" self.timer.stop() self.label.setText("Loading...") self._angle = 0 @@ -102,10 +102,10 @@ def _update_animation(self) -> None: self.update() def sizeHint(self) -> QtCore.QSize: + """Re-implemented method, size hint""" popup_width = int(self.geometry().width()) popup_height = int(self.geometry().height()) # Centering logic - popup_x = self.x() popup_y = self.y() + (self.height() - popup_height) // 2 self.move(popup_x, popup_y) @@ -113,10 +113,8 @@ def sizeHint(self) -> QtCore.QSize: self.setMinimumSize(popup_width, popup_height) return super().sizeHint() - def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None: - return - def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) # loading circle draw if self.anim_type == LoadScreen.AnimationGIF.DEFAULT: @@ -124,12 +122,8 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.setRenderHint( QtGui.QPainter.RenderHint.LosslessImageRendering, True ) - painter.setRenderHint( - QtGui.QPainter.RenderHint.SmoothPixmapTransform, True - ) - painter.setRenderHint( - QtGui.QPainter.RenderHint.TextAntialiasing, True - ) + painter.setRenderHint(QtGui.QPainter.RenderHint.SmoothPixmapTransform, True) + painter.setRenderHint(QtGui.QPainter.RenderHint.TextAntialiasing, True) pen = QtGui.QPen() pen.setWidth(8) pen.setColor(QtGui.QColor("#ffffff")) @@ -143,20 +137,17 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.translate(center_x, center_y) painter.rotate(self._angle) - arc_rect = QtCore.QRectF( - -arc_size / 2, -arc_size / 2, arc_size, arc_size - ) + arc_rect = QtCore.QRectF(-arc_size / 2, -arc_size / 2, arc_size, arc_size) span_angle = int(self._span_angle * 16) painter.drawArc(arc_rect, 0, span_angle) def resizeEvent(self, event: QtGui.QResizeEvent) -> None: + """Re-implemented method, handle widget resize event""" super().resizeEvent(event) - label_width = self.width() label_height = 100 label_x = (self.width() - label_width) // 2 label_y = int(self.height() * 0.65) - margin = 20 # Center the GIF gifshow_width = self.width() - margin * 2 @@ -167,14 +158,15 @@ def resizeEvent(self, event: QtGui.QResizeEvent) -> None: self.label.setGeometry(label_x, label_y, label_width, label_height) def show(self) -> None: - self.geometry_calc() + """Re-implemented method, show widget""" + self._geometry_calc() # Start the animation timer only if no GIF is present if self.anim_type == LoadScreen.AnimationGIF.DEFAULT: self.timer.start() self.repaint() return super().show() - def setupUI(self) -> None: + def _setupUI(self) -> None: self.gifshow = QtWidgets.QLabel("", self) self.gifshow.setObjectName("gifshow") self.gifshow.setStyleSheet("background: transparent;") diff --git a/BlocksScreen/lib/panels/widgets/loadWidget.py b/BlocksScreen/lib/panels/widgets/loadWidget.py index 4001d900..9a5f3d68 100644 --- a/BlocksScreen/lib/panels/widgets/loadWidget.py +++ b/BlocksScreen/lib/panels/widgets/loadWidget.py @@ -1,8 +1,7 @@ - from PyQt6 import QtCore, QtGui, QtWidgets -class LoadingOverlayWidget(QtWidgets.QLabel): +class LoadingOverlayWidget(QtWidgets.QLabel): def __init__( self, parent: QtWidgets.QWidget, @@ -16,7 +15,7 @@ def __init__( self.max_length = 150.0 self.length_step = 2.5 - self.setupUI() + self._setupUI() self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self._update_animation) @@ -25,10 +24,11 @@ def __init__( self.repaint() def set_status_message(self, message: str) -> None: + """Set widget message""" self.label.setText(message) - def close(self) -> bool: + """Re-implemented method, close widget""" self.timer.stop() self.label.setText("Loading...") self._angle = 0 @@ -48,19 +48,13 @@ def _update_animation(self) -> None: self._is_span_growing = True self.update() - def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True) - painter.setRenderHint( - QtGui.QPainter.RenderHint.LosslessImageRendering, True - ) - painter.setRenderHint( - QtGui.QPainter.RenderHint.SmoothPixmapTransform, True - ) - painter.setRenderHint( - QtGui.QPainter.RenderHint.TextAntialiasing, True - ) + painter.setRenderHint(QtGui.QPainter.RenderHint.LosslessImageRendering, True) + painter.setRenderHint(QtGui.QPainter.RenderHint.SmoothPixmapTransform, True) + painter.setRenderHint(QtGui.QPainter.RenderHint.TextAntialiasing, True) pen = QtGui.QPen() pen.setWidth(8) pen.setColor(QtGui.QColor("#ffffff")) @@ -74,35 +68,31 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.translate(center_x, center_y) painter.rotate(self._angle) - arc_rect = QtCore.QRectF( - -arc_size / 2, -arc_size / 2, arc_size, arc_size - ) + arc_rect = QtCore.QRectF(-arc_size / 2, -arc_size / 2, arc_size, arc_size) span_angle = int(self._span_angle * 16) painter.drawArc(arc_rect, 0, span_angle) def resizeEvent(self, event: QtGui.QResizeEvent) -> None: + """Re-implemented method, handle resize event""" super().resizeEvent(event) - label_width = self.width() label_height = 100 label_x = (self.width() - label_width) // 2 label_y = int(self.height() * 0.65) - margin = 20 # Center the GIF gifshow_width = self.width() - margin * 2 gifshow_height = self.height() - (self.height() - label_y) - margin - self.gifshow.setGeometry(margin, margin, gifshow_width, gifshow_height) - self.label.setGeometry(label_x, label_y, label_width, label_height) def show(self) -> None: + """Re-implemented method, show widget""" self.timer.start() self.repaint() return super().show() - def setupUI(self) -> None: + def _setupUI(self) -> None: self.gifshow = QtWidgets.QLabel("", self) self.gifshow.setObjectName("gifshow") self.gifshow.setStyleSheet("background: transparent;") @@ -113,4 +103,4 @@ def setupUI(self) -> None: font.setPointSize(20) self.label.setFont(font) self.label.setStyleSheet("color: #ffffff; background: transparent;") - self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) \ No newline at end of file + self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) diff --git a/BlocksScreen/lib/panels/widgets/numpadPage.py b/BlocksScreen/lib/panels/widgets/numpadPage.py index dcfe6b5b..9674084d 100644 --- a/BlocksScreen/lib/panels/widgets/numpadPage.py +++ b/BlocksScreen/lib/panels/widgets/numpadPage.py @@ -17,7 +17,7 @@ def __init__( parent, ) -> None: super().__init__(parent) - self.setupUI() + self._setupUI() self.current_value: str = "0" self.name: str = "" self.min_value: int = 0 @@ -37,9 +37,7 @@ def __init__( self.numpad_enter.clicked.connect(lambda: self.value_inserted("enter")) self.numpad_clear.clicked.connect(lambda: self.value_inserted("clear")) self.numpad_back_btn.clicked.connect(self.back_button) - self.start_glow_animation.connect( - self.inserted_value.start_glow_animation - ) + self.start_glow_animation.connect(self.inserted_value.start_glow_animation) def value_inserted(self, value: str) -> None: """Handle number insertion on the numpad @@ -59,14 +57,8 @@ def value_inserted(self, value: str) -> None: if "enter" in value and self.current_value.isnumeric(): if len(self.current_value) == 0: self.current_value = "0" - if ( - self.min_value - <= int(self.current_value) - <= self.max_value - ): - self.value_selected.emit( - self.name, int(self.current_value) - ) + if self.min_value <= int(self.current_value) <= self.max_value: + self.value_selected.emit(self.name, int(self.current_value)) self.request_back.emit() elif "clear" in value: @@ -81,8 +73,9 @@ def value_inserted(self, value: str) -> None: self.inserted_value.glow_animation.stop() self.inserted_value.setText(str(self.current_value)) - + def back_button(self): + """Request back page""" self.request_back.emit() def set_name(self, name: str) -> None: @@ -93,24 +86,25 @@ def set_name(self, name: str) -> None: self.update() def set_value(self, value: int) -> None: + """Set numpad value""" self.current_value = str(value) self.inserted_value.setText(str(value)) def set_min_value(self, min_value: int) -> None: + """Set minimum allowed value""" self.min_value = min_value self.update_min_max_label() def set_max_value(self, max_value: int) -> None: + """Set maximum allowed value""" self.max_value = max_value self.update_min_max_label() def update_min_max_label(self) -> None: """Updates the text of the min/max label.""" - self.min_max_label.setText( - f"Range: {self.min_value} - {self.max_value}" - ) - - def setupUI(self) -> None: + self.min_max_label.setText(f"Range: {self.min_value} - {self.max_value}") + + def _setupUI(self) -> None: self.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.BlankCursor)) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) @@ -151,8 +145,7 @@ def setupUI(self) -> None: self.header_layout.addWidget( self.numpad_title, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) self.numpad_back_btn = IconButton(self) @@ -168,9 +161,7 @@ def setupUI(self) -> None: self.numpad_back_btn.setSizePolicy(sizePolicy) self.numpad_back_btn.setMinimumSize(QtCore.QSize(60, 60)) self.numpad_back_btn.setMaximumSize(QtCore.QSize(60, 60)) - self.numpad_back_btn.setPixmap( - QtGui.QPixmap(":ui/media/btn_icons/back.svg") - ) + self.numpad_back_btn.setPixmap(QtGui.QPixmap(":ui/media/btn_icons/back.svg")) self.numpad_back_btn.setObjectName("numpad_back_btn") self.header_layout.addWidget( self.numpad_back_btn, @@ -230,11 +221,9 @@ def setupUI(self) -> None: self.value_and_range_layout.addWidget( self.inserted_value, 0, QtCore.Qt.AlignmentFlag.AlignCenter ) - - self.main_content_layout.addLayout( - self.value_and_range_layout, 1 - ) - + + self.main_content_layout.addLayout(self.value_and_range_layout, 1) + self.inserted_value.setBackgroundRole(QtGui.QPalette.ColorRole.Window) self.setBackgroundRole(QtGui.QPalette.ColorRole.Window) self.line = QtWidgets.QFrame(self) @@ -258,9 +247,7 @@ def setupUI(self) -> None: font.setPointSize(28) font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferDefault) self.numpad_9 = NumpadButton(self) - sizePolicy.setHeightForWidth( - self.numpad_9.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_9.sizePolicy().hasHeightForWidth()) self.numpad_9.setSizePolicy(sizePolicy) self.numpad_9.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_9.setFont(font) @@ -271,9 +258,7 @@ def setupUI(self) -> None: self.numpad_9, 0, 2, 1, 1, QtCore.Qt.AlignmentFlag.AlignLeft ) self.numpad_8 = NumpadButton(parent=self) - sizePolicy.setHeightForWidth( - self.numpad_8.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_8.sizePolicy().hasHeightForWidth()) self.numpad_8.setSizePolicy(sizePolicy) self.numpad_8.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_8.setFont(font) @@ -284,9 +269,7 @@ def setupUI(self) -> None: self.numpad_8, 0, 1, 1, 1, QtCore.Qt.AlignmentFlag.AlignHCenter ) self.numpad_7 = NumpadButton(self) - sizePolicy.setHeightForWidth( - self.numpad_7.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_7.sizePolicy().hasHeightForWidth()) self.numpad_7.setSizePolicy(sizePolicy) self.numpad_7.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_7.setFont(font) @@ -297,9 +280,7 @@ def setupUI(self) -> None: self.numpad_7, 0, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignLeft ) self.numpad_6 = NumpadButton(self) - sizePolicy.setHeightForWidth( - self.numpad_6.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_6.sizePolicy().hasHeightForWidth()) self.numpad_6.setSizePolicy(sizePolicy) self.numpad_6.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_6.setFont(font) @@ -311,9 +292,7 @@ def setupUI(self) -> None: self.numpad_6, 1, 2, 1, 1, QtCore.Qt.AlignmentFlag.AlignRight ) self.numpad_5 = NumpadButton(self) - sizePolicy.setHeightForWidth( - self.numpad_5.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_5.sizePolicy().hasHeightForWidth()) self.numpad_5.setSizePolicy(sizePolicy) self.numpad_5.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_5.setFont(font) @@ -323,9 +302,7 @@ def setupUI(self) -> None: self.numpad_5, 1, 1, 1, 1, QtCore.Qt.AlignmentFlag.AlignHCenter ) self.numpad_4 = NumpadButton(self) - sizePolicy.setHeightForWidth( - self.numpad_4.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_4.sizePolicy().hasHeightForWidth()) self.numpad_4.setSizePolicy(sizePolicy) self.numpad_4.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_4.setFont(font) @@ -336,9 +313,7 @@ def setupUI(self) -> None: self.numpad_4, 1, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignLeft ) self.numpad_3 = NumpadButton(parent=self) - sizePolicy.setHeightForWidth( - self.numpad_3.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_3.sizePolicy().hasHeightForWidth()) self.numpad_3.setSizePolicy(sizePolicy) self.numpad_3.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_3.setFont(font) @@ -349,9 +324,7 @@ def setupUI(self) -> None: self.numpad_3, 2, 2, 1, 1, QtCore.Qt.AlignmentFlag.AlignRight ) self.numpad_2 = NumpadButton(self) - sizePolicy.setHeightForWidth( - self.numpad_2.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_2.sizePolicy().hasHeightForWidth()) self.numpad_2.setSizePolicy(sizePolicy) self.numpad_2.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_2.setFont(font) @@ -362,9 +335,7 @@ def setupUI(self) -> None: self.numpad_2, 2, 1, 1, 1, QtCore.Qt.AlignmentFlag.AlignCenter ) self.numpad_1 = NumpadButton(parent=self) - sizePolicy.setHeightForWidth( - self.numpad_1.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_1.sizePolicy().hasHeightForWidth()) self.numpad_1.setSizePolicy(sizePolicy) self.numpad_1.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_1.setFont(font) @@ -375,9 +346,7 @@ def setupUI(self) -> None: self.numpad_1, 2, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignLeft ) self.numpad_0 = NumpadButton(parent=self) - sizePolicy.setHeightForWidth( - self.numpad_0.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_0.sizePolicy().hasHeightForWidth()) self.numpad_0.setSizePolicy(sizePolicy) self.numpad_0.setMinimumSize(QtCore.QSize(150, 60)) self.numpad_0.setFont(font) @@ -389,89 +358,57 @@ def setupUI(self) -> None: ) self.numpad_enter = IconButton(parent=self) self.numpad_enter.setEnabled(True) - sizePolicy.setHeightForWidth( - self.numpad_enter.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_enter.sizePolicy().hasHeightForWidth()) self.numpad_enter.setSizePolicy(sizePolicy) self.numpad_enter.setMinimumSize(QtCore.QSize(60, 60)) self.numpad_enter.setFlat(True) - self.numpad_enter.setPixmap( - QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") - ) + self.numpad_enter.setPixmap(QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg")) self.numpad_enter.setObjectName("numpad_enter") self.button_grid_layout.addWidget( self.numpad_enter, 3, 0, 1, 1, QtCore.Qt.AlignmentFlag.AlignCenter ) self.numpad_clear = IconButton(parent=self) - sizePolicy.setHeightForWidth( - self.numpad_clear.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.numpad_clear.sizePolicy().hasHeightForWidth()) self.numpad_clear.setSizePolicy(sizePolicy) self.numpad_clear.setMinimumSize(QtCore.QSize(60, 60)) self.numpad_clear.setFlat(True) - self.numpad_clear.setPixmap( - QtGui.QPixmap(":/dialog/media/btn_icons/no.svg") - ) + self.numpad_clear.setPixmap(QtGui.QPixmap(":/dialog/media/btn_icons/no.svg")) self.numpad_clear.setObjectName("numpad_clear") self.button_grid_layout.addWidget( self.numpad_clear, 3, 2, 1, 1, QtCore.Qt.AlignmentFlag.AlignCenter ) - self.button_grid_layout.setAlignment( - QtCore.Qt.AlignmentFlag.AlignCenter - ) + self.button_grid_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.main_content_layout.addLayout(self.button_grid_layout) - self.main_content_layout.setAlignment( - QtCore.Qt.AlignmentFlag.AlignCenter - ) + self.main_content_layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.setLayout(self.main_content_layout) - self.retranslateUI() + self._retranslateUI() QtCore.QMetaObject.connectSlotsByName(self) - def retranslateUI(self) -> None: + def _retranslateUI(self) -> None: _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("customNumpad", "Form")) - self.numpad_title.setText( - _translate("customNumpad", "Target Temperature") - ) + self.numpad_title.setText(_translate("customNumpad", "Target Temperature")) self.numpad_back_btn.setProperty( "button_type", _translate("customNumpad", "icon") ) self.numpad_6.setText(_translate("customNumpad", "6")) - self.numpad_6.setProperty( - "position", _translate("customNumpad", "right") - ) + self.numpad_6.setProperty("position", _translate("customNumpad", "right")) self.numpad_9.setText(_translate("customNumpad", "9")) - self.numpad_9.setProperty( - "position", _translate("customNumpad", "right") - ) + self.numpad_9.setProperty("position", _translate("customNumpad", "right")) self.numpad_8.setText(_translate("customNumpad", "8")) self.numpad_2.setText(_translate("customNumpad", "2")) self.numpad_0.setText(_translate("customNumpad", "0")) - self.numpad_0.setProperty( - "position", _translate("customNumpad", "down") - ) + self.numpad_0.setProperty("position", _translate("customNumpad", "down")) self.numpad_3.setText(_translate("customNumpad", "3")) - self.numpad_3.setProperty( - "position", _translate("customNumpad", "right") - ) + self.numpad_3.setProperty("position", _translate("customNumpad", "right")) self.numpad_4.setText(_translate("customNumpad", "4")) - self.numpad_4.setProperty( - "position", _translate("customNumpad", "left") - ) + self.numpad_4.setProperty("position", _translate("customNumpad", "left")) self.numpad_5.setText(_translate("customNumpad", "5")) self.numpad_1.setText(_translate("customNumpad", "1")) - self.numpad_1.setProperty( - "position", _translate("customNumpad", "left") - ) - self.numpad_enter.setProperty( - "button_type", _translate("customNumpad", "icon") - ) + self.numpad_1.setProperty("position", _translate("customNumpad", "left")) + self.numpad_enter.setProperty("button_type", _translate("customNumpad", "icon")) self.numpad_7.setText(_translate("customNumpad", "7")) - self.numpad_7.setProperty( - "position", _translate("customNumpad", "left") - ) - self.numpad_clear.setProperty( - "button_type", _translate("customNumpad", "icon") - ) \ No newline at end of file + self.numpad_7.setProperty("position", _translate("customNumpad", "left")) + self.numpad_clear.setProperty("button_type", _translate("customNumpad", "icon")) diff --git a/BlocksScreen/lib/panels/widgets/optionCardWidget.py b/BlocksScreen/lib/panels/widgets/optionCardWidget.py index 81cb3bdb..711dcd02 100644 --- a/BlocksScreen/lib/panels/widgets/optionCardWidget.py +++ b/BlocksScreen/lib/panels/widgets/optionCardWidget.py @@ -25,30 +25,33 @@ def __init__( self.icon_background_color = QtGui.QColor(150, 150, 130, 80) self.name = name self.card_text = text - self.setupUi(self) - self.continue_button.clicked.connect( - lambda: self.continue_clicked.emit(self) - ) + self._setupUi(self) + self.continue_button.clicked.connect(lambda: self.continue_clicked.emit(self)) self.set_card_icon(icon) self.set_card_text(text) def disable_button(self) -> None: + """Disable widget button""" self.continue_button.setDisabled(True) self.repaint() def enable_button(self) -> None: + """Enable widget button""" self.continue_button.setEnabled(True) self.repaint() def set_card_icon(self, pixmap: QtGui.QPixmap) -> None: + """Set widget icon""" self.option_icon.setPixmap(pixmap) self.repaint() def set_card_text(self, text: str) -> None: + """Set widget text""" self.option_text.setText(text) self.repaint() def set_card_text_color(self, color: QtGui.QColor) -> None: + """Set widget text color""" self.text_color = color _palette = self.option_text.palette() _palette.setColor(QtGui.QPalette.ColorRole.WindowText, color) @@ -56,32 +59,31 @@ def set_card_text_color(self, color: QtGui.QColor) -> None: self.repaint() def set_background_color(self, color: QtGui.QColor) -> None: + """Set widget background color""" self.color = color self.repaint() - def sizeHint(self) -> QtCore.QSize: - return super().sizeHint() - - def underMouse(self) -> bool: - return super().underMouse() - def enterEvent(self, event: QtGui.QEnterEvent) -> None: + """Re-implemented method, highlight widget edges""" # Illuminate the edges to a lighter blue # To achieve this just Force update the widget self.update() return super().enterEvent(event) def leaveEvent(self, a0: QtCore.QEvent) -> None: + """Re-implemented method, disable widget edges highlight""" # Reset the color # Just as before force update the widget self.update() return super().leaveEvent(a0) def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None: + """Re-implemented method, handle mouse press event""" self.update() return super().mousePressEvent(a0) def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" # Rounded background edges self.background_path = QtGui.QPainterPath() self.background_path.addRoundedRect( @@ -136,7 +138,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.end() - def setupUi(self, option_card): + def _setupUi(self, option_card): option_card.setObjectName("option_card") option_card.resize(200, 300) sizePolicy = QtWidgets.QSizePolicy( @@ -145,9 +147,7 @@ def setupUi(self, option_card): ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth( - option_card.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(option_card.sizePolicy().hasHeightForWidth()) option_card.setSizePolicy(sizePolicy) option_card.setMinimumSize(QtCore.QSize(200, 300)) option_card.setMaximumSize(QtCore.QSize(200, 300)) @@ -169,8 +169,7 @@ def setupUi(self, option_card): self.verticalLayout.addWidget( self.line_separator, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) self.option_text = QtWidgets.QLabel(parent=option_card) self.option_text.setMinimumSize(QtCore.QSize(200, 50)) @@ -180,8 +179,7 @@ def setupUi(self, option_card): ) self.continue_button = IconButton(parent=option_card) self.option_text.setAlignment( - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter ) self.option_text.setWordWrap(True) _button_font = QtGui.QFont() @@ -211,10 +209,10 @@ def setupUi(self, option_card): self.continue_button.setObjectName("continue_button") self.verticalLayout.addWidget(self.continue_button) - self.retranslateUi(option_card) + self._retranslateUi(option_card) QtCore.QMetaObject.connectSlotsByName(option_card) - def retranslateUi(self, option_card): + def _retranslateUi(self, option_card): _translate = QtCore.QCoreApplication.translate option_card.setWindowTitle(_translate("option_card", "Frame")) self.option_text.setText(_translate("option_card", "TextLabel")) diff --git a/BlocksScreen/lib/panels/widgets/popupDialogWidget.py b/BlocksScreen/lib/panels/widgets/popupDialogWidget.py index 112d660a..f878f612 100644 --- a/BlocksScreen/lib/panels/widgets/popupDialogWidget.py +++ b/BlocksScreen/lib/panels/widgets/popupDialogWidget.py @@ -9,31 +9,31 @@ class Popup(QtWidgets.QDialog): class MessageType(enum.Enum): + """Popup Message type (level)""" + INFO = enum.auto() WARNING = enum.auto() ERROR = enum.auto() UNKNOWN = enum.auto() class ColorCode(enum.Enum): + """Popup message-color code""" + INFO = QtGui.QColor("#446CDB") WARNING = QtGui.QColor("#E7E147") ERROR = QtGui.QColor("#CA4949") def __init__(self, parent) -> None: super().__init__(parent) - - # Instance variables self.popup_timeout = BASE_POPUP_TIMEOUT self.timeout_timer = QtCore.QTimer(self) self.messages: Deque = deque() self.persistent_notifications: Deque = deque() - self.message_type: Popup.MessageType = Popup.MessageType.INFO self.default_background_color = QtGui.QColor(164, 164, 164) self.info_icon = QtGui.QPixmap(":ui/media/btn_icons/info.svg") self.warning_icon = QtGui.QPixmap(":ui/media/btn_icons/warning.svg") self.error_icon = QtGui.QPixmap(":ui/media/btn_icons/error.svg") - self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True) self.setMouseTracking(True) self.setWindowFlags( @@ -41,31 +41,26 @@ def __init__(self, parent) -> None: | QtCore.Qt.WindowType.FramelessWindowHint | QtCore.Qt.WindowType.X11BypassWindowManagerHint ) - - self.setupUI() - - + self._setupUI() self.slide_in_animation = QtCore.QPropertyAnimation(self, b"geometry") self.slide_in_animation.setDuration(1000) self.slide_in_animation.setEasingCurve(QtCore.QEasingCurve.Type.OutCubic) - - self.slide_out_animation = QtCore.QPropertyAnimation(self, b"geometry") self.slide_out_animation.setDuration(200) self.slide_out_animation.setEasingCurve(QtCore.QEasingCurve.Type.InCubic) - - self.slide_in_animation.finished.connect(self.on_slide_in_finished) self.slide_out_animation.finished.connect(self.on_slide_out_finished) self.timeout_timer.timeout.connect(self.slide_out_animation.start) def on_slide_in_finished(self): + """Handle slide in animation finished""" self.timeout_timer.start() def on_slide_out_finished(self): + """Handle slide out animation finished""" self.close() - self.add_popup() - + self._add_popup() + def _calculate_target_geometry(self) -> QtCore.QRect: app_instance = QtWidgets.QApplication.instance() main_window = app_instance.activeWindow() if app_instance else None @@ -74,28 +69,31 @@ def _calculate_target_geometry(self) -> QtCore.QRect: if isinstance(widget, QtWidgets.QMainWindow): main_window = widget break - + parent_rect = main_window.geometry() width = int(parent_rect.width() * 0.85) - height = min(self.text_label.rect().height(), self.icon_label.rect().height()) - - x = parent_rect.x() + (parent_rect.width() - width) // 2 + height = min(self.text_label.rect().height(), self.icon_label.rect().height()) + + x = parent_rect.x() + (parent_rect.width() - width) // 2 y = parent_rect.y() + 20 - + return QtCore.QRect(x, y, width, height) def updateMask(self) -> None: + """Update widget mask properties""" path = QtGui.QPainterPath() path.addRoundedRect(self.rect().toRectF(), 10, 10) region = QtGui.QRegion(path.toFillPolygon(QtGui.QTransform()).toPolygon()) self.setMask(region) def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None: + """Re-implemented method, handle mouse press events""" self.timeout_timer.stop() self.slide_out_animation.start() def set_timeout(self, value: int) -> None: + """Set popup timeout""" if not isinstance(value, int): raise ValueError("Expected type int ") self.popup_timeout = value @@ -104,25 +102,36 @@ def new_message( self, message_type: MessageType = MessageType.INFO, message: str = "", - persistent: bool = False, timeout: int = 0, ): + """Create new popup message + + Args: + message_type (MessageType, optional): Message Level, See `MessageType` Types. Defaults to MessageType.INFO. + message (str, optional): The message. Defaults to "". + timeout (int, optional): How long the message stays for, in milliseconds. Defaults to 0. + + Returns: + _type_: _description_ + """ self.messages.append( {"message": message, "type": message_type, "timeout": timeout} ) - return self.add_popup() + return self._add_popup() - def add_popup(self) -> None: + def _add_popup(self) -> None: + """Add popup to queue""" if ( self.messages - and self.slide_in_animation.state() == QtCore.QPropertyAnimation.State.Stopped - and self.slide_out_animation.state() == QtCore.QPropertyAnimation.State.Stopped + and self.slide_in_animation.state() + == QtCore.QPropertyAnimation.State.Stopped + and self.slide_out_animation.state() + == QtCore.QPropertyAnimation.State.Stopped ): message_entry = self.messages.popleft() self.message_type = message_entry.get("type") message = message_entry.get("message") self.text_label.setText(message) - match self.message_type: case Popup.MessageType.INFO: self.icon_label.setPixmap(self.info_icon) @@ -130,36 +139,31 @@ def add_popup(self) -> None: self.icon_label.setPixmap(self.warning_icon) case Popup.MessageType.ERROR: self.icon_label.setPixmap(self.error_icon) - - self.timeout_timer.setInterval( - self.popup_timeout - ) - + self.timeout_timer.setInterval(self.popup_timeout) end_rect = self._calculate_target_geometry() - - start_rect = end_rect.translated(0, -end_rect.height()) - self.slide_in_animation.setStartValue(start_rect) self.slide_in_animation.setEndValue(end_rect) self.slide_out_animation.setStartValue(end_rect) self.slide_out_animation.setEndValue(start_rect) self.setGeometry(start_rect) - self.open() def showEvent(self, a0: QtGui.QShowEvent) -> None: + """Re-implementation, widget show""" self.slide_in_animation.start() super().showEvent(a0) def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: + """Re-implementation, handle resize event""" self.updateMask() super().resizeEvent(a0) def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True) - + _base_color = self.default_background_color if self.message_type == Popup.MessageType.INFO: _base_color = Popup.ColorCode.INFO.value @@ -168,18 +172,17 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: elif self.message_type == Popup.MessageType.WARNING: _base_color = Popup.ColorCode.WARNING.value - center_point = QtCore.QPointF(self.rect().center()) gradient = QtGui.QRadialGradient(center_point, self.rect().width() / 2.0) - + gradient.setColorAt(0, _base_color) gradient.setColorAt(1.0, _base_color.darker(160)) painter.setBrush(gradient) painter.setPen(QtCore.Qt.PenStyle.NoPen) painter.drawRoundedRect(self.rect(), 10, 10) - - def setupUI(self) -> None: + + def _setupUI(self) -> None: self.vertical_layout = QtWidgets.QVBoxLayout(self) self.horizontal_layout = QtWidgets.QHBoxLayout() self.horizontal_layout.setContentsMargins(5, 5, 5, 5) @@ -192,19 +195,23 @@ def setupUI(self) -> None: self.text_label = QtWidgets.QLabel(self) self.text_label.setWordWrap(True) - self.text_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.AlignmentFlag.AlignHCenter) - + self.text_label.setAlignment( + QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.AlignmentFlag.AlignHCenter + ) + font = self.text_label.font() font.setPixelSize(18) font.setFamily("sans-serif") palette = self.text_label.palette() - palette.setColor(QtGui.QPalette.ColorRole.WindowText, QtCore.Qt.GlobalColor.white) + palette.setColor( + QtGui.QPalette.ColorRole.WindowText, QtCore.Qt.GlobalColor.white + ) self.text_label.setPalette(palette) self.text_label.setFont(font) self.spacer = QtWidgets.QSpacerItem(60, 60) - + self.horizontal_layout.addWidget(self.text_label, 1) self.horizontal_layout.addItem(self.spacer) - self.vertical_layout.addLayout(self.horizontal_layout) \ No newline at end of file + self.vertical_layout.addLayout(self.horizontal_layout) diff --git a/BlocksScreen/lib/panels/widgets/printcorePage.py b/BlocksScreen/lib/panels/widgets/printcorePage.py index 6da6814f..c2683cd1 100644 --- a/BlocksScreen/lib/panels/widgets/printcorePage.py +++ b/BlocksScreen/lib/panels/widgets/printcorePage.py @@ -1,35 +1,33 @@ from lib.utils.blocks_button import BlocksCustomButton from PyQt6 import QtCore, QtGui, QtWidgets -class SwapPrintcorePage(QtWidgets.QDialog): - - +class SwapPrintcorePage(QtWidgets.QDialog): def __init__( - self, parent: QtWidgets.QWidget, + self, + parent: QtWidgets.QWidget, ) -> None: super().__init__(parent) self.setStyleSheet( "background-image: url(:/background/media/1st_background.png);" ) self.setWindowFlags( - QtCore.Qt.WindowType.Popup - | QtCore.Qt.WindowType.FramelessWindowHint + QtCore.Qt.WindowType.Popup | QtCore.Qt.WindowType.FramelessWindowHint ) - self.setupUI() + self._setupUI() self.repaint() def setText(self, text: str) -> None: + """Set widget text""" self.label.setText(text) self.repaint() - - def Text(self) -> str: - return self.label.text() - - + def text(self) -> str: + """Return current widget text""" + return self.label.text() - def geometry_calc(self) -> None: + def _geometry_calc(self) -> None: + """Calculate widget position relative to the screen""" app_instance = QtWidgets.QApplication.instance() main_window = app_instance.activeWindow() if app_instance else None if main_window is None and app_instance: @@ -44,6 +42,7 @@ def geometry_calc(self) -> None: self.setGeometry(x, y, width, height) def sizeHint(self) -> QtCore.QSize: + """Re-implemented method, handle widget size""" popup_width = int(self.geometry().width()) popup_height = int(self.geometry().height()) # Centering logic @@ -53,39 +52,36 @@ def sizeHint(self) -> QtCore.QSize: self.move(popup_x, popup_y) self.setFixedSize(popup_width, popup_height) self.setMinimumSize(popup_width, popup_height) - + return super().sizeHint() def resizeEvent(self, event: QtGui.QResizeEvent) -> None: + """Re-implemented method, handle widget resize event""" super().resizeEvent(event) - self.tittle.setGeometry(0, 0, self.width(), 60) - - # Calculate label geometry first label_margin = 20 label_height = int(self.height() * 0.65) - label_margin - self.label.setGeometry(label_margin, 60, self.width() - 2 * label_margin, label_height) - # Calculate button geometry based on the window's dimensions + self.label.setGeometry( + label_margin, 60, self.width() - 2 * label_margin, label_height + ) button_width = 250 button_height = 80 spacing = 100 total_button_width = 2 * button_width + spacing - # Center the buttons horizontally start_x = (self.width() - total_button_width) // 2 - button_y = self.height() - button_height -45 + button_y = self.height() - button_height - 45 self.pc_accept.setGeometry(start_x, button_y, button_width, button_height) - self.pc_cancel.setGeometry(start_x + button_width+100 , button_y, button_width, button_height) + self.pc_cancel.setGeometry( + start_x + button_width + 100, button_y, button_width, button_height + ) def show(self) -> None: - self.geometry_calc() + """Re-implemented method, widget show""" + self._geometry_calc() self.repaint() return super().show() - - - - - def setupUI(self) -> None: + def _setupUI(self) -> None: font = QtGui.QFont() font.setPointSize(20) @@ -104,7 +100,9 @@ def setupUI(self) -> None: self.pc_cancel = BlocksCustomButton(parent=self) self.pc_cancel.setMinimumSize(QtCore.QSize(250, 80)) self.pc_cancel.setMaximumSize(QtCore.QSize(250, 80)) - self.pc_cancel.setProperty("icon_pixmap", QtGui.QPixmap(":/dialog/media/btn_icons/no.svg")) + self.pc_cancel.setProperty( + "icon_pixmap", QtGui.QPixmap(":/dialog/media/btn_icons/no.svg") + ) self.pc_cancel.setObjectName("pc_cancel") self.pc_cancel.setFont(font) self.pc_cancel.setText("Cancel") @@ -112,7 +110,9 @@ def setupUI(self) -> None: self.pc_accept = BlocksCustomButton(parent=self) self.pc_accept.setMinimumSize(QtCore.QSize(250, 80)) self.pc_accept.setMaximumSize(QtCore.QSize(250, 80)) - self.pc_accept.setProperty("icon_pixmap", QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg")) + self.pc_accept.setProperty( + "icon_pixmap", QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") + ) self.pc_accept.setObjectName("pc_accept") self.pc_accept.setFont(font) - self.pc_accept.setText("Continue?") \ No newline at end of file + self.pc_accept.setText("Continue?") diff --git a/BlocksScreen/lib/panels/widgets/probeHelperPage.py b/BlocksScreen/lib/panels/widgets/probeHelperPage.py index e546afdd..5eeb3a3d 100644 --- a/BlocksScreen/lib/panels/widgets/probeHelperPage.py +++ b/BlocksScreen/lib/panels/widgets/probeHelperPage.py @@ -7,7 +7,7 @@ from lib.utils.group_button import GroupButton from lib.utils.blocks_button import BlocksCustomButton -from lib.panels.widgets.loadPage import LoadScreen +from lib.panels.widgets.loadPage import LoadScreen class ProbeHelper(QtWidgets.QWidget): @@ -18,8 +18,8 @@ class ProbeHelper(QtWidgets.QWidget): str, name="run_gcode" ) - query_printer_object: typing.ClassVar[QtCore.pyqtSignal] = ( - QtCore.pyqtSignal(dict, name="query_object") + query_printer_object: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + dict, name="query_object" ) subscribe_config: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( [ @@ -46,71 +46,47 @@ class ProbeHelper(QtWidgets.QWidget): z_offset_config_method: tuple = () z_offset_calibration_speed: int = 100 - - def __init__(self, parent: QtWidgets.QWidget) -> None: super().__init__(parent) - self.Loadscreen = LoadScreen(self) - self.setObjectName("probe_offset_page") - self.setupUi() - + self._setupUi() self.inductive_icon = QtGui.QPixmap( ":/z_levelling/media/btn_icons/inductive.svg" ) - self.bltouch_icon = QtGui.QPixmap( - ":/z_levelling/media/btn_icons/bltouch.svg" - ) + self.bltouch_icon = QtGui.QPixmap(":/z_levelling/media/btn_icons/bltouch.svg") self.endstop_icon = QtGui.QPixmap( ":/extruder_related/media/btn_icons/switch_zoom.svg" ) - self.eddy_icon = QtGui.QPixmap( - ":/z_levelling/media/btn_icons/eddy_mech.svg" - ) - + self.eddy_icon = QtGui.QPixmap(":/z_levelling/media/btn_icons/eddy_mech.svg") self._toggle_tool_buttons(False) self._setup_move_option_buttons() self.move_option_1.toggled.connect( - lambda: self.handle_zhopHeight_change( - new_value=float(self.distances[0]) - ) + lambda: self.handle_zhopHeight_change(new_value=float(self.distances[0])) ) self.move_option_2.toggled.connect( - lambda: self.handle_zhopHeight_change( - new_value=float(self.distances[1]) - ) + lambda: self.handle_zhopHeight_change(new_value=float(self.distances[1])) ) self.move_option_3.toggled.connect( - lambda: self.handle_zhopHeight_change( - new_value=float(self.distances[2]) - ) + lambda: self.handle_zhopHeight_change(new_value=float(self.distances[2])) ) self.move_option_4.toggled.connect( - lambda: self.handle_zhopHeight_change( - new_value=float(self.distances[3]) - ) + lambda: self.handle_zhopHeight_change(new_value=float(self.distances[3])) ) self.move_option_5.toggled.connect( - lambda: self.handle_zhopHeight_change( - new_value=float(self.distances[4]) - ) - ) - self.mb_raise_nozzle.clicked.connect( - lambda:self.handle_nozzle_move("raise") - ) - self.mb_lower_nozzle.clicked.connect( - lambda:self.handle_nozzle_move("lower") + lambda: self.handle_zhopHeight_change(new_value=float(self.distances[4])) ) + self.mb_raise_nozzle.clicked.connect(lambda: self.handle_nozzle_move("raise")) + self.mb_lower_nozzle.clicked.connect(lambda: self.handle_nozzle_move("lower")) self.po_back_button.clicked.connect(self.request_back) self.accept_button.clicked.connect(self.handle_accept) self.abort_button.clicked.connect(self.handle_abort) self.update() - self.block_z = False self.block_list = False def on_klippy_status(self, state: str): + """Handle Klippy status event change""" if state.lower() == "standby": self.block_z = False self.block_list = False @@ -138,9 +114,9 @@ def on_klippy_status(self, state: str): if child_widget is not None: child_widget.setParent(None) child_widget.deleteLater() - return def handle_nozzle_move(self, direction: str): + """Handle move z buttons click""" if direction == "raise": self._pending_gcode = f"TESTZ Z={self._zhop_height}" elif direction == "lower": @@ -177,7 +153,9 @@ def _configure_option_cards(self, probes_list: list[str]) -> None: _card = OptionCard(self, _card_text, str(probe), _icon) # type: ignore _card.setObjectName(str(probe)) self.card_options.update({str(probe): _card}) - self.main_content_horizontal_layout.addWidget(_card, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter) + self.main_content_horizontal_layout.addWidget( + _card, alignment=QtCore.Qt.AlignmentFlag.AlignHCenter + ) if not hasattr(self.card_options.get(probe), "continue_clicked"): del _card self.card_options.pop(probe) @@ -194,22 +172,20 @@ def _hide_option_cards(self) -> None: def _show_option_cards(self) -> None: list(map(lambda x: x[1].show(), self.card_options.items())) - def init_probe_config(self) -> None: + def _init_probe_config(self) -> None: + """Initialize internal probe tracking""" if not self.z_offset_config_method: return - if self.z_offset_config_type != "endstop": self.z_offsets = tuple( map( - lambda axis: self.z_offset_config_method[1].get( - f"{axis}_offset" - ), + lambda axis: self.z_offset_config_method[1].get(f"{axis}_offset"), ["x", "y", "z"], ) ) - self.z_offset_calibration_speed = self.z_offset_config_method[ - 1 - ].get("speed") + self.z_offset_calibration_speed = self.z_offset_config_method[1].get( + "speed" + ) @QtCore.pyqtSlot(list, name="on_object_config") @QtCore.pyqtSlot(dict, name="on_object_config") @@ -224,29 +200,30 @@ def on_object_config(self, config: dict | list) -> None: return # BUG: If i don't add if not self.probe_config i'll just receive the configuration a bunch of times - if isinstance(config, list):... - # if self.block_list: - # return - # else: - # self.block_list = True - - # _keys = [] - # if not isinstance(config, list): - # return - - # list(map(lambda item: _keys.extend(item.keys()), config)) - - # probe, *_ = config[0].items() - # self.z_offset_method_type = probe[0] # The one found first - # self.z_offset_method_config = ( - # probe[1], - # "PROBE_CALIBRATE", - # "Z_OFFSET_APPLY_PROBE", - # ) - # self.init_probe_config() - # if not _keys: - # return - # self._configure_option_cards(_keys) + if isinstance(config, list): + ... + # if self.block_list: + # return + # else: + # self.block_list = True + + # _keys = [] + # if not isinstance(config, list): + # return + + # list(map(lambda item: _keys.extend(item.keys()), config)) + + # probe, *_ = config[0].items() + # self.z_offset_method_type = probe[0] # The one found first + # self.z_offset_method_config = ( + # probe[1], + # "PROBE_CALIBRATE", + # "Z_OFFSET_APPLY_PROBE", + # ) + # self.init_probe_config() + # if not _keys: + # return + # self._configure_option_cards(_keys) elif isinstance(config, dict): if config.get("stepper_z"): @@ -254,14 +231,12 @@ def on_object_config(self, config: dict | list) -> None: return else: self.block_z = True - + _virtual_endstop = "probe:z_virtual_endstop" _config = config.get("stepper_z") if not _config: return - if ( - _config.get("endstop_pin") == _virtual_endstop - ): # home with probe + if _config.get("endstop_pin") == _virtual_endstop: # home with probe return self.z_offset_config_type = "endstop" self.z_offset_config_method = ( @@ -304,6 +279,7 @@ def on_object_config(self, config: dict | list) -> None: @QtCore.pyqtSlot(dict, name="on_printer_config") def on_printer_config(self, config: dict) -> None: + """Handle received printer config""" _probe_types = [ "probe", "bltouch", @@ -326,6 +302,7 @@ def on_printer_config(self, config: dict) -> None: @QtCore.pyqtSlot(dict, name="on_available_gcode_cmds") def on_available_gcode_cmds(self, gcode_cmds: dict) -> None: + """Setup available probe calibration commands""" _available_commands = gcode_cmds.keys() if "PROBE_CALIBRATE" in _available_commands: self._calibration_commands.append("PROBE_CALIBRATE") @@ -411,7 +388,7 @@ def handle_start_tool(self, sender: typing.Type[OptionCard]) -> None: """ if not sender: return - + for i in self.card_options.values(): i.setDisabled(True) @@ -420,9 +397,7 @@ def handle_start_tool(self, sender: typing.Type[OptionCard]) -> None: if self.z_offset_safe_xy: self.run_gcode_signal.emit("G28\nM400") - self._move_to_pos( - self.z_offset_safe_xy[0], self.z_offset_safe_xy[1], 100 - ) + self._move_to_pos(self.z_offset_safe_xy[0], self.z_offset_safe_xy[1], 100) self.helper_initialize = True _timer = QtCore.QTimer() _timer.setSingleShot(True) @@ -462,7 +437,7 @@ def handle_abort(self) -> None: @QtCore.pyqtSlot(str, list, name="on_gcode_move_update") def on_gcode_move_update(self, name: str, value: list) -> None: - # TODO: catch the z distances and update the values on the window + """Handle gcode move update""" if not value: return @@ -477,6 +452,7 @@ def on_gcode_move_update(self, name: str, value: list) -> None: @QtCore.pyqtSlot(dict, name="on_manual_probe_update") def on_manual_probe_update(self, update: dict) -> None: + """Handle manual probe update""" if not update: return @@ -492,13 +468,9 @@ def on_manual_probe_update(self, update: dict) -> None: self._toggle_tool_buttons(True) if update.get("z_position_upper"): - self.old_offset_info.setText( - f"{update.get('z_position_upper'):.4f} mm" - ) + self.old_offset_info.setText(f"{update.get('z_position_upper'):.4f} mm") if update.get("z_position"): - self.current_offset_info.setText( - f"{update.get('z_position'):.4f} mm" - ) + self.current_offset_info.setText(f"{update.get('z_position'):.4f} mm") @QtCore.pyqtSlot(list, name="handle_gcode_response") def handle_gcode_response(self, data: list) -> None: @@ -508,13 +480,9 @@ def handle_gcode_response(self, data: list) -> None: data (list): A list containing the gcode that originated the response and the response """ - # TODO: Only check for messages if we are in the tool otherwise ignore them if self.isVisible(): if data[0].startswith("!!"): # An error occurred - if ( - "already in a manual z probe" - in data[0].strip("!! ").lower() - ): + if "already in a manual z probe" in data[0].strip("!! ").lower(): self._hide_option_cards() self.helper_start = True self._toggle_tool_buttons(True) @@ -527,6 +495,7 @@ def handle_gcode_response(self, data: list) -> None: @QtCore.pyqtSlot(list, name="handle_error_response") def handle_error_response(self, data: list) -> None: + """Handle received error response""" ... # _data, _metadata, *extra = data + [None] * max(0, 2 - len(data)) @@ -535,12 +504,6 @@ def _move_to_pos(self, x, y, speed) -> None: self.run_gcode_signal.emit(f"G90\nG1 X{x} Y{y} F{speed * 60}\nM400") return - ############################################################################### - ################################# UI RELATED ################################## - ############################################################################### - def show(self) -> None: - return super().show() - def _setup_move_option_buttons(self) -> None: """Change move_option_x buttons text for configured zhop values in stored in the class variable `distances` @@ -549,7 +512,6 @@ def _setup_move_option_buttons(self) -> None: """ if self.distances: return - self.move_option_1.setText(str(self.distances[0])) self.move_option_2.setText(str(self.distances[1])) self.move_option_3.setText(str(self.distances[2])) @@ -579,7 +541,12 @@ def _toggle_tool_buttons(self, state: bool) -> None: self.mb_raise_nozzle.show() self.mb_lower_nozzle.show() self.frame_2.show() - self.spacerItem.changeSize(40,20,QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + self.spacerItem.changeSize( + 40, + 20, + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Minimum, + ) else: self.po_back_button.setEnabled(True) @@ -594,13 +561,17 @@ def _toggle_tool_buttons(self, state: bool) -> None: self.mb_raise_nozzle.hide() self.mb_lower_nozzle.hide() self.frame_2.hide() - self.spacerItem.changeSize(0,0,QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) - + self.spacerItem.changeSize( + 0, + 0, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, + ) + self.update() return - - def setupUi(self) -> None: + def _setupUi(self) -> None: self.bbp_offset_value_selector_group = QtWidgets.QButtonGroup(self) self.bbp_offset_value_selector_group.setExclusive(True) sizePolicy = QtWidgets.QSizePolicy( @@ -656,9 +627,7 @@ def setupUi(self) -> None: self.accept_button.setGeometry(QtCore.QRect(480, 340, 170, 60)) self.accept_button.setText("Accept") self.accept_button.setObjectName("accept_button") - self.accept_button.setPixmap( - QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg") - ) + self.accept_button.setPixmap(QtGui.QPixmap(":/dialog/media/btn_icons/yes.svg")) self.accept_button.setVisible(False) font = QtGui.QFont() font.setPointSize(15) @@ -668,9 +637,7 @@ def setupUi(self) -> None: self.abort_button.setGeometry(QtCore.QRect(300, 340, 170, 60)) self.abort_button.setText("Abort") self.abort_button.setObjectName("accept_button") - self.abort_button.setPixmap( - QtGui.QPixmap(":/dialog/media/btn_icons/no.svg") - ) + self.abort_button.setPixmap(QtGui.QPixmap(":/dialog/media/btn_icons/no.svg")) self.abort_button.setVisible(False) font = QtGui.QFont() font.setPointSize(15) @@ -698,16 +665,13 @@ def setupUi(self) -> None: self.po_back_button.setMaximumSize(QtCore.QSize(60, 60)) self.po_back_button.setText("") self.po_back_button.setFlat(True) - self.po_back_button.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/back.svg") - ) + self.po_back_button.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back.svg")) self.po_back_button.setObjectName("po_back_button") self.bbp_header_layout.addWidget( self.po_back_button, 0, - QtCore.Qt.AlignmentFlag.AlignRight - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter, ) self.bbp_header_layout.setStretch(0, 1) self.verticalLayout.addLayout(self.bbp_header_layout) @@ -724,9 +688,6 @@ def setupUi(self) -> None: self.separator_line.setObjectName("separator_line") self.verticalLayout.addWidget(self.separator_line) - - - # Offset Steps Buttons Group Box (LEFT side of main_content_horizontal_layout) self.bbp_offset_steps_buttons_group_box = QtWidgets.QGroupBox(self) font = QtGui.QFont() @@ -748,9 +709,7 @@ def setupUi(self) -> None: self.bbp_offset_steps_buttons.setObjectName("bbp_offset_steps_buttons") # 0.1mm button - self.move_option_1 = GroupButton( - parent=self.bbp_offset_steps_buttons_group_box - ) + self.move_option_1 = GroupButton(parent=self.bbp_offset_steps_buttons_group_box) self.move_option_1.setMinimumSize(QtCore.QSize(100, 60)) self.move_option_1.setMaximumSize(QtCore.QSize(100, 60)) self.move_option_1.setText("0.01 mm") @@ -763,22 +722,15 @@ def setupUi(self) -> None: self.move_option_1.setFlat(True) self.move_option_1.setProperty("button_type", "") self.move_option_1.setObjectName("move_option_1") - self.bbp_offset_value_selector_group.addButton( - self.move_option_1 - ) + self.bbp_offset_value_selector_group.addButton(self.move_option_1) self.bbp_offset_steps_buttons.addWidget( self.move_option_1, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) - - # 0.01mm button - self.move_option_2 = GroupButton( - parent=self.bbp_offset_steps_buttons_group_box - ) + self.move_option_2 = GroupButton(parent=self.bbp_offset_steps_buttons_group_box) self.move_option_2.setMinimumSize(QtCore.QSize(100, 60)) self.move_option_2.setMaximumSize( QtCore.QSize(100, 60) @@ -792,20 +744,15 @@ def setupUi(self) -> None: self.move_option_2.setFlat(True) self.move_option_2.setProperty("button_type", "") self.move_option_2.setObjectName("move_option_2") - self.bbp_offset_value_selector_group.addButton( - self.move_option_2 - ) + self.bbp_offset_value_selector_group.addButton(self.move_option_2) self.bbp_offset_steps_buttons.addWidget( self.move_option_2, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # 0.05mm button - self.move_option_3 = GroupButton( - parent=self.bbp_offset_steps_buttons_group_box - ) + self.move_option_3 = GroupButton(parent=self.bbp_offset_steps_buttons_group_box) self.move_option_3.setMinimumSize(QtCore.QSize(100, 60)) self.move_option_3.setMaximumSize( QtCore.QSize(100, 60) @@ -819,20 +766,15 @@ def setupUi(self) -> None: self.move_option_3.setFlat(True) self.move_option_3.setProperty("button_type", "") self.move_option_3.setObjectName("move_option_3") - self.bbp_offset_value_selector_group.addButton( - self.move_option_3 - ) + self.bbp_offset_value_selector_group.addButton(self.move_option_3) self.bbp_offset_steps_buttons.addWidget( self.move_option_3, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # 0.025mm button - self.move_option_4 = GroupButton( - parent=self.bbp_offset_steps_buttons_group_box - ) + self.move_option_4 = GroupButton(parent=self.bbp_offset_steps_buttons_group_box) self.move_option_4.setMinimumSize(QtCore.QSize(100, 60)) self.move_option_4.setMaximumSize( QtCore.QSize(100, 60) @@ -846,20 +788,15 @@ def setupUi(self) -> None: self.move_option_4.setFlat(True) self.move_option_4.setProperty("button_type", "") self.move_option_4.setObjectName("move_option_4") - self.bbp_offset_value_selector_group.addButton( - self.move_option_4 - ) + self.bbp_offset_value_selector_group.addButton(self.move_option_4) self.bbp_offset_steps_buttons.addWidget( self.move_option_4, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) - # 0.01mm button - self.move_option_5 = GroupButton( - parent=self.bbp_offset_steps_buttons_group_box - ) + # 0.01mm button + self.move_option_5 = GroupButton(parent=self.bbp_offset_steps_buttons_group_box) self.move_option_5.setMinimumSize(QtCore.QSize(100, 60)) self.move_option_5.setMaximumSize( QtCore.QSize(100, 60) @@ -873,22 +810,17 @@ def setupUi(self) -> None: self.move_option_5.setFlat(True) self.move_option_5.setProperty("button_type", "") self.move_option_5.setObjectName("move_option_4") - self.bbp_offset_value_selector_group.addButton( - self.move_option_5 - ) + self.bbp_offset_value_selector_group.addButton(self.move_option_5) self.bbp_offset_steps_buttons.addWidget( self.move_option_5, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # Line separator for 0.025mm - set size policy to expanding horizontally # Set the layout for the group box - self.bbp_offset_steps_buttons_group_box.setLayout( - self.bbp_offset_steps_buttons - ) + self.bbp_offset_steps_buttons_group_box.setLayout(self.bbp_offset_steps_buttons) # Add the group box to the main content horizontal layout FIRST for left placement self.main_content_horizontal_layout.addWidget( self.bbp_offset_steps_buttons_group_box @@ -896,9 +828,7 @@ def setupUi(self) -> None: # Graphic and Current Value Frame (This will now be in the MIDDLE) self.frame_2 = QtWidgets.QFrame(parent=self) - sizePolicy.setHeightForWidth( - self.frame_2.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) self.frame_2.setSizePolicy(sizePolicy) self.frame_2.setMinimumSize(QtCore.QSize(350, 160)) self.frame_2.setMaximumSize(QtCore.QSize(350, 160)) @@ -907,33 +837,25 @@ def setupUi(self) -> None: self.frame_2.setObjectName("frame_2") self.tool_image = QtWidgets.QLabel(parent=self.frame_2) self.tool_image.setGeometry(QtCore.QRect(0, 30, 371, 121)) - self.tool_image.setLayoutDirection( - QtCore.Qt.LayoutDirection.RightToLeft - ) + self.tool_image.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) self.tool_image.setPixmap( QtGui.QPixmap(":/graphics/media/graphics/babystep_graphic.png") ) self.tool_image.setScaledContents(False) - self.tool_image.setAlignment( - QtCore.Qt.AlignmentFlag.AlignCenter - ) + self.tool_image.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.tool_image.setObjectName("tool_image") # === NEW LABEL ADDED HERE === # This is the title label that appears above the red value box. self.old_offset_info = QtWidgets.QLabel(parent=self.frame_2) # Position it just above the red box. Red box is at y=70, so y=40 is appropriate. - self.old_offset_info.setGeometry( - QtCore.QRect(240, 95, 200, 60) - ) + self.old_offset_info.setGeometry(QtCore.QRect(240, 95, 200, 60)) font = QtGui.QFont() font.setPointSize(12) self.old_offset_info.setFont(font) # Set color to white to be visible on the dark background - self.old_offset_info.setStyleSheet( - "color: gray; background: transparent;" - ) + self.old_offset_info.setStyleSheet("color: gray; background: transparent;") self.old_offset_info.setText("Z-Offset") self.old_offset_info.setObjectName("old_offset_info") self.old_offset_info.setText("0 mm") @@ -941,9 +863,7 @@ def setupUi(self) -> None: # === END OF NEW LABEL === self.current_offset_info = BlocksLabel(parent=self.frame_2) - self.current_offset_info.setGeometry( - QtCore.QRect(100, 70, 200, 60) - ) + self.current_offset_info.setGeometry(QtCore.QRect(100, 70, 200, 60)) sizePolicy.setHeightForWidth( self.current_offset_info.sizePolicy().hasHeightForWidth() ) @@ -953,25 +873,18 @@ def setupUi(self) -> None: font = QtGui.QFont() font.setPointSize(14) self.current_offset_info.setFont(font) - self.current_offset_info.setStyleSheet( - "background: transparent; color: white;" - ) + self.current_offset_info.setStyleSheet("background: transparent; color: white;") self.current_offset_info.setText("Z:0mm") self.current_offset_info.setPixmap( QtGui.QPixmap(":/graphics/media/btn_icons/z_offset_adjust.svg") ) - self.current_offset_info.setAlignment( - QtCore.Qt.AlignmentFlag.AlignCenter - ) - self.current_offset_info.setObjectName( - "current_offset_info" - ) + self.current_offset_info.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + self.current_offset_info.setObjectName("current_offset_info") # Add graphic frame AFTER the offset buttons group box self.main_content_horizontal_layout.addWidget( self.frame_2, 0, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) # Move Buttons Layout (This will now be on the RIGHT) @@ -1025,7 +938,6 @@ def setupUi(self) -> None: # Add move buttons layout LAST for right placement self.main_content_horizontal_layout.addLayout(self.bbp_buttons_layout) - self.main_content_horizontal_layout.addItem(self.spacerItem) # Set stretch factors for main content horizontal layout diff --git a/BlocksScreen/lib/panels/widgets/sensorWidget.py b/BlocksScreen/lib/panels/widgets/sensorWidget.py index 109792fa..5223ac83 100644 --- a/BlocksScreen/lib/panels/widgets/sensorWidget.py +++ b/BlocksScreen/lib/panels/widgets/sensorWidget.py @@ -7,18 +7,26 @@ class SensorWidget(QtWidgets.QWidget): class SensorType(enum.Enum): + """Filament sensor type""" + SWITCH = enum.auto() MOTION = enum.auto() class SensorFlags(enum.Flag): + """Filament sensor flags""" + CLICKABLE = enum.auto() DISPLAY = enum.auto() class FilamentState(enum.Enum): + """Current filament state, sensor has or does not have filament""" + MISSING = 0 PRESENT = 1 class SensorState(enum.IntEnum): + """Current sensor filament state, if it's turned on or not""" + OFF = False ON = True @@ -40,14 +48,10 @@ def __init__(self, parent, sensor_name: str): self.filament_state: SensorWidget.FilamentState = ( SensorWidget.FilamentState.MISSING ) - self.sensor_state: SensorWidget.SensorState = ( - SensorWidget.SensorState.OFF - ) + self.sensor_state: SensorWidget.SensorState = SensorWidget.SensorState.OFF self._icon_label = None self._text_label = None - self._text: str = ( - str(self.sensor_type.name) + " Sensor: " + str(self.name) - ) + self._text: str = str(self.sensor_type.name) + " Sensor: " + str(self.name) self._item_rect: QtCore.QRect = QtCore.QRect() self.icon_pixmap_fp: QtGui.QPixmap = QtGui.QPixmap( ":/filament_related/media/btn_icons/filament_sensor_turn_on.svg" @@ -55,10 +59,11 @@ def __init__(self, parent, sensor_name: str): self.icon_pixmap_fnp: QtGui.QPixmap = QtGui.QPixmap( ":/filament_related/media/btn_icons/filament_sensor_off.svg" ) - self.setupUI() + self._setupUI() @property def type(self) -> SensorType: + """Sensor type""" return self._sensor_type @type.setter @@ -67,6 +72,7 @@ def type(self, type: SensorType): @property def flags(self) -> SensorFlags: + """Current filament sensor flags""" return self._flags @flags.setter @@ -75,6 +81,7 @@ def flags(self, flags: SensorFlags) -> None: @property def text(self) -> str: + """Filament sensor text""" return self._text @text.setter @@ -85,13 +92,12 @@ def text(self, new_text) -> None: @QtCore.pyqtSlot(bool, name="change_fil_sensor_state") def change_fil_sensor_state(self, state: FilamentState): + """Change filament sensor state""" if isinstance(state, SensorWidget.FilamentState): self.filament_state = state - def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: - return super().resizeEvent(a0) - def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" # if ( # self._scaled_select_on_pixmap is not None # and self._scaled_select_off_pixmap is not None @@ -103,9 +109,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: # ) style_painter = QtWidgets.QStylePainter(self) - style_painter.setRenderHint( - style_painter.RenderHint.Antialiasing, True - ) + style_painter.setRenderHint(style_painter.RenderHint.Antialiasing, True) style_painter.setRenderHint( style_painter.RenderHint.SmoothPixmapTransform, True ) @@ -142,6 +146,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: @property def toggle_sensor_gcode_command(self) -> str: + """Toggle filament sensor""" self.sensor_state = ( SensorWidget.SensorState.ON if self.sensor_state == SensorWidget.SensorState.OFF @@ -151,7 +156,7 @@ def toggle_sensor_gcode_command(self) -> str: f"SET_FILAMENT_SENSOR SENSOR={self.text} ENABLE={not self.sensor_state.value}" ) - def setupUI(self): + def _setupUI(self): _policy = QtWidgets.QSizePolicy.Policy.MinimumExpanding size_policy = QtWidgets.QSizePolicy(_policy, _policy) size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) @@ -160,9 +165,7 @@ def setupUI(self): self.sensor_horizontal_layout.setGeometry(QtCore.QRect(0, 0, 640, 60)) self.sensor_horizontal_layout.setObjectName("sensorHorizontalLayout") self._icon_label = BlocksLabel(self) - size_policy.setHeightForWidth( - self._icon_label.sizePolicy().hasHeightForWidth() - ) + size_policy.setHeightForWidth(self._icon_label.sizePolicy().hasHeightForWidth()) self._icon_label.setSizePolicy(size_policy) self._icon_label.setMinimumSize(60, 60) self._icon_label.setMaximumSize(60, 60) @@ -173,18 +176,14 @@ def setupUI(self): ) self.sensor_horizontal_layout.addWidget(self._icon_label) self._text_label = QtWidgets.QLabel(parent=self) - size_policy.setHeightForWidth( - self._text_label.sizePolicy().hasHeightForWidth() - ) + size_policy.setHeightForWidth(self._text_label.sizePolicy().hasHeightForWidth()) self._text_label.setMinimumSize(100, 60) self._text_label.setMaximumSize(500, 60) _font = QtGui.QFont() _font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) _font.setPointSize(18) palette = self._text_label.palette() - palette.setColor( - palette.ColorRole.WindowText, QtGui.QColorConstants.White - ) + palette.setColor(palette.ColorRole.WindowText, QtGui.QColorConstants.White) self._text_label.setPalette(palette) self._text_label.setFont(_font) self._text_label.setText(str(self._text)) diff --git a/BlocksScreen/lib/panels/widgets/sensorsPanel.py b/BlocksScreen/lib/panels/widgets/sensorsPanel.py index a40247b8..51a5b2c1 100644 --- a/BlocksScreen/lib/panels/widgets/sensorsPanel.py +++ b/BlocksScreen/lib/panels/widgets/sensorsPanel.py @@ -9,10 +9,8 @@ class SensorsWindow(QtWidgets.QWidget): run_gcode_signal: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( str, name="run_gcode" ) - change_fil_sensor_state: typing.ClassVar[QtCore.pyqtSignal] = ( - QtCore.pyqtSignal( - SensorWidget.FilamentState, name="change_fil_sensor_state" - ) + change_fil_sensor_state: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + SensorWidget.FilamentState, name="change_fil_sensor_state" ) request_back: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( name="request_back" @@ -21,10 +19,8 @@ class SensorsWindow(QtWidgets.QWidget): def __init__(self, parent): super(SensorsWindow, self).__init__(parent) - self.setupUi() - self.setAttribute( - QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True - ) + self._setupUi() + self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) self.setTabletTracking(True) self.fs_sensors_list.itemClicked.connect(self.handle_sensor_clicked) @@ -33,33 +29,36 @@ def __init__(self, parent): @QtCore.pyqtSlot(dict, name="handle_available_fil_sensors") def handle_available_fil_sensors(self, sensors: dict) -> None: + """Handle available filament sensors, create `SensorWidget` for each detected + sensor + """ if not isinstance(sensors, dict): return - filtered_sensors = list( - filter( - lambda printer_obj: str(printer_obj).startswith("filament_switch_sensor") - or str(printer_obj).startswith("filament_motion_sensor"), - sensors.keys(), + filter( + lambda printer_obj: str(printer_obj).startswith( + "filament_switch_sensor" + ) + or str(printer_obj).startswith("filament_motion_sensor"), + sensors.keys(), ) ) - if filtered_sensors: self.fs_sensors_list.setRowHidden(self.fs_sensors_list.row(self.item), True) self.sensor_list = [ self.create_sensor_widget(name=sensor) for sensor in filtered_sensors ] else: - self.fs_sensors_list.setRowHidden(self.fs_sensors_list.row(self.item), False) - - + self.fs_sensors_list.setRowHidden( + self.fs_sensors_list.row(self.item), False + ) @QtCore.pyqtSlot(str, str, bool, name="handle_fil_state_change") def handle_fil_state_change( self, sensor_name: str, parameter: str, value: bool ) -> None: + """Handle filament state chage""" if sensor_name in self.sensor_list: - state = SensorWidget.FilamentState(value) _split = sensor_name.split(" ") _item = self.fs_sensors_list.findChild( SensorWidget, @@ -70,26 +69,21 @@ def handle_fil_state_change( if isinstance(_item, SensorWidget) and hasattr( _item, "change_fil_sensor_state" ): - _item.change_fil_sensor_state( - SensorWidget.FilamentState.PRESENT - ) + _item.change_fil_sensor_state(SensorWidget.FilamentState.PRESENT) _item.repaint() elif parameter == "filament_missing": if isinstance(_item, SensorWidget) and hasattr( _item, "change_fil_sensor_state" ): - _item.change_fil_sensor_state( - SensorWidget.FilamentState.MISSING - ) + _item.change_fil_sensor_state(SensorWidget.FilamentState.MISSING) _item.repaint() elif parameter == "enabled": if _item and isinstance(_item, SensorWidget): - self.run_gcode_signal.emit( - _item.toggle_sensor_gcode_command - ) + self.run_gcode_signal.emit(_item.toggle_sensor_gcode_command) @QtCore.pyqtSlot(QtWidgets.QListWidgetItem, name="handle_sensor_clicked") def handle_sensor_clicked(self, sensor: QtWidgets.QListWidgetItem) -> None: + """Handle filament sensor clicked""" _item = self.fs_sensors_list.itemWidget(sensor) # FIXME: This is just not working _item.toggle_button.state = ~_item.toggle_button.state @@ -116,9 +110,7 @@ def create_sensor_widget(self, name: str) -> SensorWidget: return _item_widget - def paintEvent(self, a0: QtGui.QPaintEvent) -> None: ... - - def setupUi(self): + def _setupUi(self): self.setObjectName("filament_sensors_page") sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Policy.MinimumExpanding, @@ -147,9 +139,7 @@ def setupUi(self): font = QtGui.QFont() font.setPointSize(22) palette = QtGui.QPalette() - palette.setColor( - palette.ColorRole.WindowText, QtGui.QColorConstants.White - ) + palette.setColor(palette.ColorRole.WindowText, QtGui.QColorConstants.White) self.fs_page_title.setPalette(palette) self.fs_page_title.setFont(font) self.fs_page_title.setObjectName("fs_page_title") @@ -162,9 +152,7 @@ def setupUi(self): self.fs_back_button.setMinimumSize(QtCore.QSize(60, 60)) self.fs_back_button.setMaximumSize(QtCore.QSize(60, 60)) self.fs_back_button.setFlat(True) - self.fs_back_button.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/back.svg") - ) + self.fs_back_button.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back.svg")) self.fs_back_button.setObjectName("fs_back_button") self.fs_header_layout.addWidget( self.fs_back_button, @@ -184,24 +172,17 @@ def setupUi(self): self.fs_sensors_list.setSizePolicy(sizePolicy) self.fs_sensors_list.setMinimumSize(QtCore.QSize(650, 300)) self.fs_sensors_list.setMaximumSize(QtCore.QSize(700, 300)) - self.fs_sensors_list.setLayoutDirection( - QtCore.Qt.LayoutDirection.LeftToRight - ) + self.fs_sensors_list.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) self.fs_sensors_list.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) self.fs_sensors_list.setObjectName("fs_sensors_list") - self.fs_sensors_list.setViewMode( - self.fs_sensors_list.ViewMode.ListMode - ) + self.fs_sensors_list.setViewMode(self.fs_sensors_list.ViewMode.ListMode) self.fs_sensors_list.setItemAlignment( - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter ) self.fs_sensors_list.setFlow(self.fs_sensors_list.Flow.TopToBottom) self.fs_sensors_list.setFrameStyle(0) palette = self.fs_sensors_list.palette() - palette.setColor( - palette.ColorRole.Base, QtGui.QColorConstants.Transparent - ) + palette.setColor(palette.ColorRole.Base, QtGui.QColorConstants.Transparent) self.fs_sensors_list.setPalette(palette) self.fs_sensors_list.setDropIndicatorShown(False) self.fs_sensors_list.setAcceptDrops(False) @@ -211,34 +192,31 @@ def setupUi(self): self.content_vertical_layout.addWidget( self.fs_sensors_list, 1, - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter, + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, ) font = QtGui.QFont() font.setPointSize(25) - self.item = QtWidgets.QListWidgetItem() - self.item.setSizeHint(QtCore.QSize(self.fs_sensors_list.width(),self.fs_sensors_list.height())) - + self.item.setSizeHint( + QtCore.QSize(self.fs_sensors_list.width(), self.fs_sensors_list.height()) + ) self.label = QtWidgets.QLabel("No sensors found") self.label.setFont(font) - self.label.setStyleSheet("color: gray;") + self.label.setStyleSheet("color: gray;") self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.label.hide() self.fs_sensors_list.addItem(self.item) - self.fs_sensors_list.setItemWidget(self.item,self.label) - - + self.fs_sensors_list.setItemWidget(self.item, self.label) self.content_vertical_layout.addSpacing(5) self.setLayout(self.content_vertical_layout) - self.retranslateUi() + self._retranslateUi() - def retranslateUi(self): + def _retranslateUi(self): _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("filament_sensors_page", "Form")) self.fs_page_title.setText( diff --git a/BlocksScreen/lib/panels/widgets/slider_selector_page.py b/BlocksScreen/lib/panels/widgets/slider_selector_page.py index 02f9006a..793b7044 100644 --- a/BlocksScreen/lib/panels/widgets/slider_selector_page.py +++ b/BlocksScreen/lib/panels/widgets/slider_selector_page.py @@ -27,26 +27,20 @@ def __init__(self, parent) -> None: self.decrease_button_icon = QtGui.QPixmap( ":/arrow_icons/media/btn_icons/left_arrow.svg" ) - self.background = QtGui.QPixmap( - ":/ui/background/media/1st_background.png" - ) + self.background = QtGui.QPixmap(":/ui/background/media/1st_background.png") self.setStyleSheet( "#SliderPage{background-image: url(:/background/media/1st_background.png);}\n" ) self.setObjectName("SliderPage") - self.setupUI() + self._setupUI() self.back_button.clicked.connect(self.request_back.emit) self.back_button.clicked.connect(self.value_selected.disconnect) self.slider.valueChanged.connect(self.on_slider_value_change) self.increase_button.pressed.connect( - lambda: ( - self.slider.setSliderPosition(self.slider.sliderPosition() + 5) - ) + lambda: (self.slider.setSliderPosition(self.slider.sliderPosition() + 5)) ) self.decrease_button.pressed.connect( - lambda: ( - self.slider.setSliderPosition(self.slider.sliderPosition() - 5) - ) + lambda: (self.slider.setSliderPosition(self.slider.sliderPosition() - 5)) ) self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) @@ -64,9 +58,11 @@ def set_slider_position(self, value: int) -> None: self.slider.setSliderPosition(int(value)) def set_slider_minimum(self, value: int) -> None: + """Set slider minimum value""" self.slider.setMinimum(value) def set_slider_maximum(self, value: int) -> None: + """Set slider maximum value""" self.slider.setMaximum(value) def paintEvent(self, a0: QtGui.QPaintEvent) -> None: @@ -78,21 +74,9 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.drawPixmap(self.rect(), self.background, self.rect()) self.current_value_label.setText(str(self.slider.value()) + " " + "%") self.object_name_label.setText(str(self.name)) - # if "speed" in self.name.lower(): - # # REFACTOR: Change this, so it's not hardcoded to be with objects named "speed. " - # # Range should increase however if a flag is set, if the maximum is above 100 - # # then increase the range of the slider after it is set to the maximum - # if ( - # self.slider.maximum() <= self.max_value - # and self.slider.sliderPosition() + 10 >= self.slider.maximum() - # ): - # self.slider.setMaximum(int(int(self.slider.maximum()) + 100)) - # elif self.slider.maximum() <= 100: - # self.slider.setMaximum(100) - painter.end() - def setupUI(self) -> None: + def _setupUI(self) -> None: """Setup the components for the widget""" self.setMinimumSize(QtCore.QSize(700, 410)) self.setMaximumSize(QtCore.QSize(720, 420)) @@ -131,18 +115,13 @@ def setupUI(self) -> None: self.object_name_label.setFont(font) self.object_name_label.setPalette(palette) self.object_name_label.setMinimumSize(QtCore.QSize(self.width(), 60)) - self.object_name_label.setMaximumSize( - QtCore.QSize(self.width() - 60, 60) - ) + self.object_name_label.setMaximumSize(QtCore.QSize(self.width() - 60, 60)) self.object_name_label.setAlignment( - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter ) self.back_button = IconButton(self) - self.back_button.setPixmap( - QtGui.QPixmap(":ui/media/btn_icons/back.svg") - ) + self.back_button.setPixmap(QtGui.QPixmap(":ui/media/btn_icons/back.svg")) self.back_button.has_text = False self.back_button.setMinimumSize(QtCore.QSize(60, 60)) self.back_button.setMaximumSize(QtCore.QSize(60, 60)) @@ -160,12 +139,9 @@ def setupUI(self) -> None: self.current_value_label.setFont(font) self.current_value_label.setPalette(palette) self.current_value_label.setMinimumSize(QtCore.QSize(self.width(), 80)) - self.current_value_label.setMaximumSize( - QtCore.QSize(self.width(), 300) - ) + self.current_value_label.setMaximumSize(QtCore.QSize(self.width(), 300)) self.current_value_label.setAlignment( - QtCore.Qt.AlignmentFlag.AlignHCenter - | QtCore.Qt.AlignmentFlag.AlignVCenter + QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter ) self.middle_content_layout.addWidget( self.current_value_label, @@ -189,8 +165,7 @@ def setupUI(self) -> None: self.slider_layout.addWidget( self.slider, 0, - QtCore.Qt.AlignmentFlag.AlignVCenter - | QtCore.Qt.AlignmentFlag.AlignHCenter, + QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.AlignmentFlag.AlignHCenter, ) self.increase_button = IconButton(self) self.increase_button.setProperty( diff --git a/BlocksScreen/lib/panels/widgets/troubleshootPage.py b/BlocksScreen/lib/panels/widgets/troubleshootPage.py index 6a9b4886..0c327ac7 100644 --- a/BlocksScreen/lib/panels/widgets/troubleshootPage.py +++ b/BlocksScreen/lib/panels/widgets/troubleshootPage.py @@ -2,9 +2,11 @@ from lib.utils.icon_button import IconButton + class TroubleshootPage(QtWidgets.QDialog): def __init__( - self, parent: QtWidgets.QWidget, + self, + parent: QtWidgets.QWidget, ) -> None: super().__init__(parent) self.setStyleSheet( @@ -16,23 +18,23 @@ def __init__( """ ) self.setWindowFlags( - QtCore.Qt.WindowType.Popup - | QtCore.Qt.WindowType.FramelessWindowHint + QtCore.Qt.WindowType.Popup | QtCore.Qt.WindowType.FramelessWindowHint + ) + self._setupUI() + self.label_4.setText( + "For more information check our website \n www.blockstec.com \n or \nsupport@blockstec.com" ) - self.setupUI() - self.label_4.setText("For more information check our website \n www.blockstec.com \n or \nsupport@blockstec.com") - - self.repaint() - def geometry_calc(self) -> None: + def _geometry_calc(self) -> None: + """Calculate widget position relative to the screen""" app_instance = QtWidgets.QApplication.instance() main_window = app_instance.activeWindow() if app_instance else None if main_window is None and app_instance: for widget in app_instance.allWidgets(): if isinstance(widget, QtWidgets.QMainWindow): main_window = widget - if main_window: + if main_window: x = main_window.geometry().x() y = main_window.geometry().y() width = main_window.width() @@ -40,21 +42,32 @@ def geometry_calc(self) -> None: self.setGeometry(x, y, width, height) def show(self) -> None: - self.geometry_calc() + """Re-implemented method, widget show""" + self._geometry_calc() self.repaint() return super().show() - def setupUI(self) -> None: + def _setupUI(self) -> None: self.setObjectName("troubleshoot_page") self.verticalLayout = QtWidgets.QVBoxLayout(self) self.verticalLayout.setObjectName("verticalLayout") self.leds_slider_header_layout_2 = QtWidgets.QHBoxLayout() self.leds_slider_header_layout_2.setObjectName("leds_slider_header_layout_2") - spacerItem18 = QtWidgets.QSpacerItem(60, 60, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum) + spacerItem18 = QtWidgets.QSpacerItem( + 60, + 60, + QtWidgets.QSizePolicy.Policy.Minimum, + QtWidgets.QSizePolicy.Policy.Minimum, + ) self.leds_slider_header_layout_2.addItem(spacerItem18) - spacerItem19 = QtWidgets.QSpacerItem(181, 60, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + spacerItem19 = QtWidgets.QSpacerItem( + 181, + 60, + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Minimum, + ) self.leds_slider_header_layout_2.addItem(spacerItem19) - self.tb_tittle_label = QtWidgets.QLabel("Troubleshoot",parent=self) + self.tb_tittle_label = QtWidgets.QLabel("Troubleshoot", parent=self) self.tb_tittle_label.setMinimumSize(QtCore.QSize(0, 60)) self.tb_tittle_label.setMaximumSize(QtCore.QSize(16777215, 60)) font = QtGui.QFont() @@ -65,10 +78,18 @@ def setupUI(self) -> None: self.tb_tittle_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.tb_tittle_label.setObjectName("tb_tittle_label") self.leds_slider_header_layout_2.addWidget(self.tb_tittle_label) - spacerItem20 = QtWidgets.QSpacerItem(0, 60, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum) + spacerItem20 = QtWidgets.QSpacerItem( + 0, + 60, + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Minimum, + ) self.leds_slider_header_layout_2.addItem(spacerItem20) self.tb_back_btn = IconButton(parent=self) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.MinimumExpanding, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.tb_back_btn.sizePolicy().hasHeightForWidth()) @@ -88,7 +109,9 @@ def setupUI(self) -> None: self.tb_back_btn.setStyleSheet("") self.tb_back_btn.setAutoDefault(False) self.tb_back_btn.setFlat(True) - self.tb_back_btn.setProperty("icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg")) + self.tb_back_btn.setProperty( + "icon_pixmap", QtGui.QPixmap(":/ui/media/btn_icons/back.svg") + ) self.tb_back_btn.setObjectName("tb_back_btn") self.leds_slider_header_layout_2.addWidget(self.tb_back_btn) self.verticalLayout.addLayout(self.leds_slider_header_layout_2) @@ -96,8 +119,11 @@ def setupUI(self) -> None: self.horizontalLayout.setObjectName("horizontalLayout") self.verticalLayout_10 = QtWidgets.QVBoxLayout() self.verticalLayout_10.setObjectName("verticalLayout_10") - self.label_4 = QtWidgets.QLabel("idk whar to type this",parent=self) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding) + self.label_4 = QtWidgets.QLabel("idk whar to type this", parent=self) + sizePolicy = QtWidgets.QSizePolicy( + QtWidgets.QSizePolicy.Policy.Expanding, + QtWidgets.QSizePolicy.Policy.Expanding, + ) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth()) @@ -110,11 +136,4 @@ def setupUI(self) -> None: self.label_4.setObjectName("label_4") self.verticalLayout_10.addWidget(self.label_4) self.horizontalLayout.addLayout(self.verticalLayout_10) - # self.widget = QtWidgets.QWidget(parent=self) - # self.widget.setMinimumSize(QtCore.QSize(300, 300)) - # self.widget.setMaximumSize(QtCore.QSize(300, 300)) - # self.widget.setAutoFillBackground(True) - # self.widget.setStyleSheet("color:white") - # self.widget.setObjectName("widget") - # self.horizontalLayout.addWidget(self.widget) - self.verticalLayout.addLayout(self.horizontalLayout) \ No newline at end of file + self.verticalLayout.addLayout(self.horizontalLayout) diff --git a/BlocksScreen/lib/panels/widgets/tunePage.py b/BlocksScreen/lib/panels/widgets/tunePage.py index ece1e770..cb9a00c8 100644 --- a/BlocksScreen/lib/panels/widgets/tunePage.py +++ b/BlocksScreen/lib/panels/widgets/tunePage.py @@ -12,8 +12,8 @@ class TuneWidget(QtWidgets.QWidget): name="request_back_page" ) - request_sensorsPage: typing.ClassVar[QtCore.pyqtSignal] = ( - QtCore.pyqtSignal(name="request_sensorsPage") + request_sensorsPage: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( + name="request_sensorsPage" ) request_bbpPage: typing.ClassVar[QtCore.pyqtSignal] = QtCore.pyqtSignal( name="request_bbpPage" @@ -40,14 +40,12 @@ class TuneWidget(QtWidgets.QWidget): def __init__(self, parent) -> None: super().__init__(parent) self.setObjectName("tune_page") - self.setupUI() + self._setupUI() self.sensors_menu_btn.clicked.connect(self.request_sensorsPage.emit) self.tune_babystep_menu_btn.clicked.connect(self.request_bbpPage.emit) self.tune_back_btn.clicked.connect(self.request_back) self.bed_display.clicked.connect( - lambda: self.request_numpad[ - str, int, "PyQt_PyObject", int, int - ].emit( + lambda: self.request_numpad[str, int, "PyQt_PyObject", int, int].emit( "Bed", int(round(self.bed_target)), self.on_numpad_change, @@ -56,9 +54,7 @@ def __init__(self, parent) -> None: ) ) self.extruder_display.clicked.connect( - lambda: self.request_numpad[ - str, int, "PyQt_PyObject", int, int - ].emit( + lambda: self.request_numpad[str, int, "PyQt_PyObject", int, int].emit( "Extruder", int(round(self.extruder_target)), self.on_numpad_change, @@ -67,9 +63,7 @@ def __init__(self, parent) -> None: ) ) self.speed_display.clicked.connect( - lambda: self.request_sliderPage[ - str, int, "PyQt_PyObject", int, int - ].emit( + lambda: self.request_sliderPage[str, int, "PyQt_PyObject", int, int].emit( "Speed", int(self.speed_factor_override * 100), self.on_slider_change, @@ -80,16 +74,16 @@ def __init__(self, parent) -> None: @QtCore.pyqtSlot(str, int, name="on_numpad_change") def on_numpad_change(self, name: str, new_value: int) -> None: + """Handle numpad value inserted""" if "bed" in name.lower(): name = "heater_bed" elif "extruder" in name.lower(): name = "extruder" - self.run_gcode.emit( - f"SET_HEATER_TEMPERATURE HEATER={name} TARGET={new_value}" - ) + self.run_gcode.emit(f"SET_HEATER_TEMPERATURE HEATER={name} TARGET={new_value}") @QtCore.pyqtSlot(str, int, name="on_slider_change") def on_slider_change(self, name: str, new_value: int) -> None: + """Handle slider page value inserted""" if "speed" in name.lower(): self.speed_factor_override = new_value / 100 self.run_gcode.emit(f"M220 S{new_value}") @@ -156,16 +150,12 @@ def on_fan_object_update( ) else: _new_display_button.setDisabled(True) - self.tune_display_vertical_child_layout_2.addWidget( - _new_display_button - ) + self.tune_display_vertical_child_layout_2.addWidget(_new_display_button) _display_button = self.tune_display_buttons.get(name) if not _display_button: return _display_button.update({"speed": int(round(new_value * 100))}) - _display_button.get("display_button").setText( - f"{new_value * 100:.0f}%" - ) + _display_button.get("display_button").setText(f"{new_value * 100:.0f}%") def create_display_button(self, name: str) -> DisplayButton: """Create and return a DisplayButton @@ -187,11 +177,10 @@ def create_display_button(self, name: str) -> DisplayButton: @QtCore.pyqtSlot(str, float, name="on_gcode_move_update") def on_gcode_move_update(self, field: str, value: float) -> None: + """Handle gcode move update""" if "speed_factor" in field: self.speed_factor_override = value - self.speed_display.setText( - str(f"{int(self.speed_factor_override * 100)}%") - ) + self.speed_display.setText(str(f"{int(self.speed_factor_override * 100)}%")) @QtCore.pyqtSlot(str, str, float, name="on_extruder_update") def on_extruder_temperature_change( @@ -226,12 +215,11 @@ def on_heater_bed_temperature_change( self.bed_target = int(new_value) def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" if self.isVisible(): - self.speed_display.setText( - str(f"{int(self.speed_factor_override * 100)}%") - ) + self.speed_display.setText(str(f"{int(self.speed_factor_override * 100)}%")) - def setupUI(self) -> None: + def _setupUI(self) -> None: sizePolicy = QtWidgets.QSizePolicy( QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.MinimumExpanding, @@ -259,9 +247,7 @@ def setupUI(self) -> None: self.tune_title_label.setFont(font) self.tune_title_label.setPalette(palette) - self.tune_title_label.setLayoutDirection( - QtCore.Qt.LayoutDirection.RightToLeft - ) + self.tune_title_label.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) self.tune_title_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.tune_title_label.setObjectName("tune_title_label") self.tune_header.addWidget( @@ -273,9 +259,7 @@ def setupUI(self) -> None: self.tune_back_btn.setMinimumSize(QtCore.QSize(60, 60)) self.tune_back_btn.setMaximumSize(QtCore.QSize(60, 60)) self.tune_back_btn.setFlat(True) - self.tune_back_btn.setPixmap( - QtGui.QPixmap(":/ui/media/btn_icons/back.svg") - ) + self.tune_back_btn.setPixmap(QtGui.QPixmap(":/ui/media/btn_icons/back.svg")) self.tune_header.addWidget( self.tune_back_btn, 0, @@ -332,9 +316,7 @@ def setupUI(self) -> None: self.tune_change_filament_btn.setAutoDefault(False) self.tune_change_filament_btn.setFlat(True) self.tune_change_filament_btn.setPixmap( - QtGui.QPixmap( - ":/filament_related/media/btn_icons/change_filament.svg" - ) + QtGui.QPixmap(":/filament_related/media/btn_icons/change_filament.svg") ) self.tune_change_filament_btn.setObjectName("tune_change_filament_btn") self.tune_menu_buttons.addWidget( @@ -355,46 +337,34 @@ def setupUI(self) -> None: self.sensors_menu_btn.setContextMenuPolicy( QtCore.Qt.ContextMenuPolicy.NoContextMenu ) - self.sensors_menu_btn.setLayoutDirection( - QtCore.Qt.LayoutDirection.LeftToRight - ) + self.sensors_menu_btn.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) self.sensors_menu_btn.setAutoDefault(False) self.sensors_menu_btn.setFlat(True) self.sensors_menu_btn.setPixmap( - QtGui.QPixmap( - ":/filament_related/media/btn_icons/filament_sensor.svg" - ) + QtGui.QPixmap(":/filament_related/media/btn_icons/filament_sensor.svg") ) self.sensors_menu_btn.setObjectName("sensors_menu_btn") self.tune_menu_buttons.addWidget(self.sensors_menu_btn) self.tune_content.addLayout(self.tune_menu_buttons, 0) self.tune_display_horizontal_parent_layout = QtWidgets.QHBoxLayout() - self.tune_display_horizontal_parent_layout.setContentsMargins( - 2, 0, 2, 2 - ) + self.tune_display_horizontal_parent_layout.setContentsMargins(2, 0, 2, 2) self.tune_display_horizontal_parent_layout.setObjectName( "tune_display_horizontal_parent_layout" ) self.tune_display_vertical_child_layout_1 = QtWidgets.QVBoxLayout() - self.tune_display_vertical_child_layout_1.setContentsMargins( - 2, 0, 2, 2 - ) + self.tune_display_vertical_child_layout_1.setContentsMargins(2, 0, 2, 2) self.tune_display_vertical_child_layout_1.setSpacing(5) self.tune_display_vertical_child_layout_1.setObjectName( "tune_display_vertical_parent_layout" ) self.tune_display_vertical_child_layout_2 = QtWidgets.QVBoxLayout() - self.tune_display_vertical_child_layout_2.setContentsMargins( - 2, 0, 2, 2 - ) + self.tune_display_vertical_child_layout_2.setContentsMargins(2, 0, 2, 2) self.tune_display_vertical_child_layout_2.setSpacing(5) self.tune_display_vertical_child_layout_2.setObjectName( "tune_display_vertical_parent_layout_2" ) self.tune_display_vertical_child_layout_3 = QtWidgets.QVBoxLayout() - self.tune_display_vertical_child_layout_3.setContentsMargins( - 2, 0, 2, 2 - ) + self.tune_display_vertical_child_layout_3.setContentsMargins(2, 0, 2, 2) self.tune_display_vertical_child_layout_3.setSpacing(5) self.tune_display_vertical_child_layout_3.setObjectName( "tune_display_vertical_parent_layout_3" @@ -409,15 +379,11 @@ def setupUI(self) -> None: self.tune_display_vertical_child_layout_3 ) self.tune_display_horizontal_parent_layout.setSpacing(0) - self.tune_display_horizontal_parent_layout.setContentsMargins( - 0, 0, 0, 0 - ) + self.tune_display_horizontal_parent_layout.setContentsMargins(0, 0, 0, 0) self.bed_display = DisplayButton(parent=self) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(1) - sizePolicy.setHeightForWidth( - self.bed_display.sizePolicy().hasHeightForWidth() - ) + sizePolicy.setHeightForWidth(self.bed_display.sizePolicy().hasHeightForWidth()) self.bed_display.setSizePolicy(sizePolicy) self.bed_display.setMinimumSize(QtCore.QSize(150, 60)) self.bed_display.setMaximumSize(QtCore.QSize(150, 60)) @@ -426,9 +392,7 @@ def setupUI(self) -> None: self.bed_display.setText("") self.bed_display.setFlat(True) self.bed_display.setPixmap( - QtGui.QPixmap( - ":/temperature_related/media/btn_icons/temperature_plate.svg" - ) + QtGui.QPixmap(":/temperature_related/media/btn_icons/temperature_plate.svg") ) self.bed_display.setObjectName("bed_display") self.tune_display_vertical_child_layout_1.addWidget(self.bed_display) @@ -443,14 +407,10 @@ def setupUI(self) -> None: self.extruder_display.setText("") self.extruder_display.setFlat(True) self.extruder_display.setPixmap( - QtGui.QPixmap( - ":/temperature_related/media/btn_icons/temperature.svg" - ) + QtGui.QPixmap(":/temperature_related/media/btn_icons/temperature.svg") ) self.extruder_display.setObjectName("extruder_display") - self.tune_display_vertical_child_layout_1.addWidget( - self.extruder_display - ) + self.tune_display_vertical_child_layout_1.addWidget(self.extruder_display) self.speed_display = DisplayButton(parent=self) self.speed_display.setFont(font) sizePolicy.setHeightForWidth( @@ -466,9 +426,7 @@ def setupUI(self) -> None: ) self.speed_display.setObjectName("speed_display") self.tune_display_vertical_child_layout_3.addWidget(self.speed_display) - self.tune_content.addLayout( - self.tune_display_horizontal_parent_layout, 1 - ) + self.tune_content.addLayout(self.tune_display_horizontal_parent_layout, 1) self.tune_display_horizontal_parent_layout.setStretch(0, 0) self.tune_display_horizontal_parent_layout.setStretch(1, 0) self.tune_display_horizontal_parent_layout.setStretch(2, 1) @@ -478,9 +436,9 @@ def setupUI(self) -> None: self.tune_content.setContentsMargins(2, 0, 2, 0) self.setLayout(self.tune_content) self.setContentsMargins(2, 2, 2, 2) - self.retranslateUI() + self._retranslateUI() - def retranslateUI(self): + def _retranslateUI(self): _translate = QtCore.QCoreApplication.translate self.setWindowTitle(_translate("printStackedWidget", "StackedWidget")) self.tune_title_label.setText(_translate("printStackedWidget", "Tune")) diff --git a/BlocksScreen/lib/panels/widgets/updatePage.py b/BlocksScreen/lib/panels/widgets/updatePage.py index 7b725514..534bbabe 100644 --- a/BlocksScreen/lib/panels/widgets/updatePage.py +++ b/BlocksScreen/lib/panels/widgets/updatePage.py @@ -239,7 +239,7 @@ def handle_update_message(self, message: dict) -> None: elif self.ongoing_update or complete: self.ongoing_update = False self.update_end.emit() - + cli_version_info = message.get("version_info", None) if not cli_version_info: return diff --git a/BlocksScreen/lib/printer.py b/BlocksScreen/lib/printer.py index c3df0d8a..52706fd5 100644 --- a/BlocksScreen/lib/printer.py +++ b/BlocksScreen/lib/printer.py @@ -464,7 +464,7 @@ def _heater_fan_object_updated(self, value: dict, fan_name: str = "") -> None: # Associated with a heater, on when heater is active # Parameters same as a normal fan _names = ["heater_fan", fan_name] - object_name = " ".join(_names) + # object_name = " ".join(_names) def _idle_timeout_object_updated( self, value: dict, name: str = "idle_timeout" diff --git a/BlocksScreen/lib/qrcode_gen.py b/BlocksScreen/lib/qrcode_gen.py index 3720f261..1901cef1 100644 --- a/BlocksScreen/lib/qrcode_gen.py +++ b/BlocksScreen/lib/qrcode_gen.py @@ -4,9 +4,7 @@ BLOCKS_URL = "https://blockstec.com" RF50_MANUAL_PAGE = "https://blockstec.com/RF50" RF50_PRODUCT_PAGE = "https://blockstec.com/rf-50" -RF50_DATASHEET_PAGE = ( - "https://www.blockstec.com/assets/downloads/rf50_datasheet.pdf" -) +RF50_DATASHEET_PAGE = "https://www.blockstec.com/assets/downloads/rf50_datasheet.pdf" RF50_DATASHEET_PAGE = "https://blockstec.com/assets/files/rf50_user_manual.pdf" @@ -28,5 +26,7 @@ def make_qrcode(data) -> ImageQt.ImageQt: def generate_wifi_qrcode( ssid: str, password: str, auth_type: str, hidden: bool = False ) -> ImageQt.ImageQt: - wifi_data = f"WIFI:T:{auth_type};S:{ssid};P:{password};{'H:true;' if hidden else ''};" + wifi_data = ( + f"WIFI:T:{auth_type};S:{ssid};P:{password};{'H:true;' if hidden else ''};" + ) return make_qrcode(wifi_data) diff --git a/BlocksScreen/lib/ui/instructionsWindow.ui b/BlocksScreen/lib/ui/instructionsWindow.ui deleted file mode 100644 index 17c4c46f..00000000 --- a/BlocksScreen/lib/ui/instructionsWindow.ui +++ /dev/null @@ -1,377 +0,0 @@ - - - utilitiesStackedWidget - - - - 0 - 0 - 798 - 417 - - - - StackedWidget - - - 1 - - - - - - 300 - 60 - 241 - 61 - - - - - Momcake - 24 - - - - background: transparent; color: white; - - - Switch Print Cores - - - title_text - - - - - true - - - - 200 - 170 - 411 - 91 - - - - - Montserrat - 14 - - - - background: transparent; color: white; - - - Printer Heating, wait to switch - - - - - - 680 - 40 - 101 - 71 - - - - - 10 - 10 - - - - - Momcake - 20 - false - PreferAntialias - - - - false - - - true - - - Qt::NoContextMenu - - - Qt::LeftToRight - - - - - - Cancel - - - false - - - true - - - menu_btn - - - :/button_borders/media/buttons/btn_part1.svg - - - :/button_borders/media/buttons/btn_part2.svg - - - :/button_borders/media/buttons/btn_part3.svg - - - normal - - - - - - - - 270 - 60 - 271 - 61 - - - - - Momcake - 24 - - - - background: transparent; color: white; - - - ROUTINE CHECK - - - title_text - - - - - true - - - - 200 - 180 - 411 - 91 - - - - - Montserrat - 14 - - - - background: transparent; color: white; - - - Do this - - - - - - 680 - 40 - 101 - 71 - - - - - 10 - 10 - - - - - Momcake - 20 - false - PreferAntialias - - - - false - - - true - - - Qt::NoContextMenu - - - Qt::LeftToRight - - - - - - Cancel - - - false - - - true - - - menu_btn - - - :/button_borders/media/buttons/btn_part1.svg - - - :/button_borders/media/buttons/btn_part2.svg - - - :/button_borders/media/buttons/btn_part3.svg - - - normal - - - - - - - - 240 - 60 - 271 - 61 - - - - - Momcake - 24 - - - - background: transparent; color: white; - - - AXES MAINTENANCE - - - title_text - - - - - true - - - - 170 - 180 - 411 - 91 - - - - - Montserrat - 14 - - - - background: transparent; color: white; - - - Use oil here - - - - - - 650 - 40 - 101 - 71 - - - - - 10 - 10 - - - - - Momcake - 20 - false - PreferAntialias - - - - false - - - true - - - Qt::NoContextMenu - - - Qt::LeftToRight - - - - - - Cancel - - - false - - - true - - - menu_btn - - - :/button_borders/media/buttons/btn_part1.svg - - - :/button_borders/media/buttons/btn_part2.svg - - - :/button_borders/media/buttons/btn_part3.svg - - - normal - - - - - - - BlocksCustomButton - QPushButton -
lib.utils.ui
-
-
- - - - - -
diff --git a/BlocksScreen/lib/ui/instructionsWindow_ui.py b/BlocksScreen/lib/ui/instructionsWindow_ui.py deleted file mode 100644 index 1198cb0c..00000000 --- a/BlocksScreen/lib/ui/instructionsWindow_ui.py +++ /dev/null @@ -1,162 +0,0 @@ -# Form implementation generated from reading ui file '/home/bugo/github/Blocks_Screen/BlocksScreen/lib/ui/instructionsWindow.ui' -# -# Created by: PyQt6 UI code generator 6.4.2 -# -# WARNING: Any manual changes made to this file will be lost when pyuic6 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt6 import QtCore, QtGui, QtWidgets - - -class Ui_utilitiesStackedWidget(object): - def setupUi(self, utilitiesStackedWidget): - utilitiesStackedWidget.setObjectName("utilitiesStackedWidget") - utilitiesStackedWidget.resize(798, 417) - self.switch_pc_page = QtWidgets.QWidget() - self.switch_pc_page.setObjectName("switch_pc_page") - self.switch_pc_title_label = QtWidgets.QLabel(parent=self.switch_pc_page) - self.switch_pc_title_label.setGeometry(QtCore.QRect(300, 60, 241, 61)) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(24) - self.switch_pc_title_label.setFont(font) - self.switch_pc_title_label.setStyleSheet("background: transparent; color: white;") - self.switch_pc_title_label.setObjectName("switch_pc_title_label") - self.switch_pc_text_label = QtWidgets.QLabel(parent=self.switch_pc_page) - self.switch_pc_text_label.setEnabled(True) - self.switch_pc_text_label.setGeometry(QtCore.QRect(200, 170, 411, 91)) - font = QtGui.QFont() - font.setFamily("Montserrat") - font.setPointSize(14) - self.switch_pc_text_label.setFont(font) - self.switch_pc_text_label.setStyleSheet("background: transparent; color: white;") - self.switch_pc_text_label.setObjectName("switch_pc_text_label") - self.switch_pc_cancel_btn = BlocksCustomButton(parent=self.switch_pc_page) - self.switch_pc_cancel_btn.setGeometry(QtCore.QRect(680, 40, 101, 71)) - self.switch_pc_cancel_btn.setMinimumSize(QtCore.QSize(10, 10)) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(20) - font.setItalic(False) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) - self.switch_pc_cancel_btn.setFont(font) - self.switch_pc_cancel_btn.setMouseTracking(False) - self.switch_pc_cancel_btn.setTabletTracking(True) - self.switch_pc_cancel_btn.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.switch_pc_cancel_btn.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) - self.switch_pc_cancel_btn.setStyleSheet("") - self.switch_pc_cancel_btn.setAutoDefault(False) - self.switch_pc_cancel_btn.setFlat(True) - self.switch_pc_cancel_btn.setProperty("borderLeftPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part1.svg")) - self.switch_pc_cancel_btn.setProperty("borderCenterPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part2.svg")) - self.switch_pc_cancel_btn.setProperty("borderRightPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part3.svg")) - self.switch_pc_cancel_btn.setObjectName("switch_pc_cancel_btn") - utilitiesStackedWidget.addWidget(self.switch_pc_page) - self.routine_check_page = QtWidgets.QWidget() - self.routine_check_page.setObjectName("routine_check_page") - self.routine_check_calib_title_label = QtWidgets.QLabel(parent=self.routine_check_page) - self.routine_check_calib_title_label.setGeometry(QtCore.QRect(270, 60, 271, 61)) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(24) - self.routine_check_calib_title_label.setFont(font) - self.routine_check_calib_title_label.setStyleSheet("background: transparent; color: white;") - self.routine_check_calib_title_label.setObjectName("routine_check_calib_title_label") - self.routine_check_text_label = QtWidgets.QLabel(parent=self.routine_check_page) - self.routine_check_text_label.setEnabled(True) - self.routine_check_text_label.setGeometry(QtCore.QRect(200, 180, 411, 91)) - font = QtGui.QFont() - font.setFamily("Montserrat") - font.setPointSize(14) - self.routine_check_text_label.setFont(font) - self.routine_check_text_label.setStyleSheet("background: transparent; color: white;") - self.routine_check_text_label.setObjectName("routine_check_text_label") - self.routine_check_cancel_btn = BlocksCustomButton(parent=self.routine_check_page) - self.routine_check_cancel_btn.setGeometry(QtCore.QRect(680, 40, 101, 71)) - self.routine_check_cancel_btn.setMinimumSize(QtCore.QSize(10, 10)) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(20) - font.setItalic(False) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) - self.routine_check_cancel_btn.setFont(font) - self.routine_check_cancel_btn.setMouseTracking(False) - self.routine_check_cancel_btn.setTabletTracking(True) - self.routine_check_cancel_btn.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.routine_check_cancel_btn.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) - self.routine_check_cancel_btn.setStyleSheet("") - self.routine_check_cancel_btn.setAutoDefault(False) - self.routine_check_cancel_btn.setFlat(True) - self.routine_check_cancel_btn.setProperty("borderLeftPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part1.svg")) - self.routine_check_cancel_btn.setProperty("borderCenterPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part2.svg")) - self.routine_check_cancel_btn.setProperty("borderRightPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part3.svg")) - self.routine_check_cancel_btn.setObjectName("routine_check_cancel_btn") - utilitiesStackedWidget.addWidget(self.routine_check_page) - self.axes_maintenance_page = QtWidgets.QWidget() - self.axes_maintenance_page.setObjectName("axes_maintenance_page") - self.axes_maintenance_calib_title_label = QtWidgets.QLabel(parent=self.axes_maintenance_page) - self.axes_maintenance_calib_title_label.setGeometry(QtCore.QRect(240, 60, 271, 61)) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(24) - self.axes_maintenance_calib_title_label.setFont(font) - self.axes_maintenance_calib_title_label.setStyleSheet("background: transparent; color: white;") - self.axes_maintenance_calib_title_label.setObjectName("axes_maintenance_calib_title_label") - self.axes_maintenance_text_label = QtWidgets.QLabel(parent=self.axes_maintenance_page) - self.axes_maintenance_text_label.setEnabled(True) - self.axes_maintenance_text_label.setGeometry(QtCore.QRect(170, 180, 411, 91)) - font = QtGui.QFont() - font.setFamily("Montserrat") - font.setPointSize(14) - self.axes_maintenance_text_label.setFont(font) - self.axes_maintenance_text_label.setStyleSheet("background: transparent; color: white;") - self.axes_maintenance_text_label.setObjectName("axes_maintenance_text_label") - self.axes_maintenance_cancel_btn = BlocksCustomButton(parent=self.axes_maintenance_page) - self.axes_maintenance_cancel_btn.setGeometry(QtCore.QRect(650, 40, 101, 71)) - self.axes_maintenance_cancel_btn.setMinimumSize(QtCore.QSize(10, 10)) - font = QtGui.QFont() - font.setFamily("Momcake") - font.setPointSize(20) - font.setItalic(False) - font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) - self.axes_maintenance_cancel_btn.setFont(font) - self.axes_maintenance_cancel_btn.setMouseTracking(False) - self.axes_maintenance_cancel_btn.setTabletTracking(True) - self.axes_maintenance_cancel_btn.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) - self.axes_maintenance_cancel_btn.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) - self.axes_maintenance_cancel_btn.setStyleSheet("") - self.axes_maintenance_cancel_btn.setAutoDefault(False) - self.axes_maintenance_cancel_btn.setFlat(True) - self.axes_maintenance_cancel_btn.setProperty("borderLeftPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part1.svg")) - self.axes_maintenance_cancel_btn.setProperty("borderCenterPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part2.svg")) - self.axes_maintenance_cancel_btn.setProperty("borderRightPixmap", QtGui.QPixmap(":/button_borders/media/buttons/btn_part3.svg")) - self.axes_maintenance_cancel_btn.setObjectName("axes_maintenance_cancel_btn") - utilitiesStackedWidget.addWidget(self.axes_maintenance_page) - - self.retranslateUi(utilitiesStackedWidget) - utilitiesStackedWidget.setCurrentIndex(1) - QtCore.QMetaObject.connectSlotsByName(utilitiesStackedWidget) - - def retranslateUi(self, utilitiesStackedWidget): - _translate = QtCore.QCoreApplication.translate - utilitiesStackedWidget.setWindowTitle(_translate("utilitiesStackedWidget", "StackedWidget")) - self.switch_pc_title_label.setText(_translate("utilitiesStackedWidget", "Switch Print Cores")) - self.switch_pc_title_label.setProperty("class", _translate("utilitiesStackedWidget", "title_text")) - self.switch_pc_text_label.setText(_translate("utilitiesStackedWidget", "Printer Heating, wait to switch")) - self.switch_pc_cancel_btn.setText(_translate("utilitiesStackedWidget", "Cancel")) - self.switch_pc_cancel_btn.setProperty("class", _translate("utilitiesStackedWidget", "menu_btn")) - self.switch_pc_cancel_btn.setProperty("button_type", _translate("utilitiesStackedWidget", "normal")) - self.routine_check_calib_title_label.setText(_translate("utilitiesStackedWidget", "ROUTINE CHECK")) - self.routine_check_calib_title_label.setProperty("class", _translate("utilitiesStackedWidget", "title_text")) - self.routine_check_text_label.setText(_translate("utilitiesStackedWidget", "Do this")) - self.routine_check_cancel_btn.setText(_translate("utilitiesStackedWidget", "Cancel")) - self.routine_check_cancel_btn.setProperty("class", _translate("utilitiesStackedWidget", "menu_btn")) - self.routine_check_cancel_btn.setProperty("button_type", _translate("utilitiesStackedWidget", "normal")) - self.axes_maintenance_calib_title_label.setText(_translate("utilitiesStackedWidget", "AXES MAINTENANCE")) - self.axes_maintenance_calib_title_label.setProperty("class", _translate("utilitiesStackedWidget", "title_text")) - self.axes_maintenance_text_label.setText(_translate("utilitiesStackedWidget", "Use oil here")) - self.axes_maintenance_cancel_btn.setText(_translate("utilitiesStackedWidget", "Cancel")) - self.axes_maintenance_cancel_btn.setProperty("class", _translate("utilitiesStackedWidget", "menu_btn")) - self.axes_maintenance_cancel_btn.setProperty("button_type", _translate("utilitiesStackedWidget", "normal")) -from lib.utils.ui import BlocksCustomButton diff --git a/BlocksScreen/lib/utils/RepeatedTimer.py b/BlocksScreen/lib/utils/RepeatedTimer.py index ff96a0f8..42b42aa0 100644 --- a/BlocksScreen/lib/utils/RepeatedTimer.py +++ b/BlocksScreen/lib/utils/RepeatedTimer.py @@ -31,6 +31,7 @@ def _run(self): self._function(*self._args, **self._kwargs) def startTimer(self): + """Start timer""" if self.running is False: try: self._timer = threading.Timer(self._timeout, self._run) @@ -47,6 +48,7 @@ def startTimer(self): self.running = True def stopTimer(self): + """Stop timer""" if self._timer is None: return if self.running: @@ -55,15 +57,3 @@ def stopTimer(self): self._timer = None self.stopEvent.clear() self.running = False - - @staticmethod - def pauseTimer(self): - # TODO never tested - self.stopEvent.clear() - self.running = False - - @staticmethod - def resumeTimer(self): - # TODO: never tested - self.stopEvent.set() - self.running = True diff --git a/BlocksScreen/lib/utils/RoutingQueue.py b/BlocksScreen/lib/utils/RoutingQueue.py index c7b2b08f..4efde01a 100644 --- a/BlocksScreen/lib/utils/RoutingQueue.py +++ b/BlocksScreen/lib/utils/RoutingQueue.py @@ -22,6 +22,7 @@ def __init__(self): @property def resend(self): + """Resend queue""" return self._resend @resend.setter @@ -30,10 +31,12 @@ def resend(self, new_resend): self._resend = new_resend def block(self): + """Blocks queue""" # Sets the flag to false self._clear_to_move.clear() def unblock(self): + """Unblock queue""" # Sets the flag to True self._clear_to_move.set() @@ -62,8 +65,8 @@ def add_command( self._read_lines += 1 except Exception as e: raise ValueError( - "Unexpected error while adding a command to queue, and argument " - ) + "Unexpected error while adding a command to queue, and argument %s" + ) from e def get_command(self, block=True, timeout=None, resend=False): """ diff --git a/BlocksScreen/lib/utils/blocks_Scrollbar.py b/BlocksScreen/lib/utils/blocks_Scrollbar.py index 08dc6b68..38e571e0 100644 --- a/BlocksScreen/lib/utils/blocks_Scrollbar.py +++ b/BlocksScreen/lib/utils/blocks_Scrollbar.py @@ -8,6 +8,7 @@ def __init__(self, parent=None): self.setFixedWidth(40) def paintEvent(self, event): + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(painter.RenderHint.Antialiasing, True) painter.setRenderHint(painter.RenderHint.SmoothPixmapTransform, True) @@ -43,15 +44,10 @@ def paintEvent(self, event): / (max_val - min_val) ) else: - val = ( - np.interp((handle_percentage), [15, 85], [0, 100]) - / 100 - * max_val - ) + val = np.interp((handle_percentage), [15, 85], [0, 100]) / 100 * max_val base_handle_length = int( - (groove.height() * page_step / (max_val - min_val + page_step)) - + 40 + (groove.height() * page_step / (max_val - min_val + page_step)) + 40 ) handle_pos = int( (groove.height() - base_handle_length) diff --git a/BlocksScreen/lib/utils/blocks_button.py b/BlocksScreen/lib/utils/blocks_button.py index dc8a79f6..141ee1ec 100644 --- a/BlocksScreen/lib/utils/blocks_button.py +++ b/BlocksScreen/lib/utils/blocks_button.py @@ -4,6 +4,8 @@ class ButtonColors(enum.Enum): + """Standard button colors""" + NORMAL_BG = (223, 223, 223) PRESSED_BG = (169, 169, 169) DISABLED_BG = (169, 169, 169) @@ -33,6 +35,7 @@ def __init__( self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) def setShowNotification(self, show: bool) -> None: + """Set notification on button""" if self._show_notification != show: self._show_notification = show self.repaint() @@ -40,6 +43,7 @@ def setShowNotification(self, show: bool) -> None: @property def name(self): + """Button name""" return self._name @name.setter @@ -48,18 +52,22 @@ def name(self, new_name) -> None: self.setObjectName(new_name) def text(self) -> str | None: + """Button text""" return self._text def setText(self, text: str) -> None: + """Set button text""" self._text = text self.update() return def setPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set button pixmap""" self.icon_pixmap = pixmap self.repaint() def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: + """Handle mouse press events""" if not self.isEnabled(): e.ignore() return @@ -75,9 +83,7 @@ def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: return super().mousePressEvent(e) def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): - opt = QtWidgets.QStyleOptionButton() - # self.initStyleOption(opt) - + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(painter.RenderHint.Antialiasing, True) painter.setRenderHint(painter.RenderHint.SmoothPixmapTransform, True) @@ -89,8 +95,6 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): if _style is None or _rect is None: return - - margin = _style.pixelMetric(_style.PixelMetric.PM_ButtonMargin, opt, self) # Determine background and text colors based on state if not self.isEnabled(): bg_color_tuple = ButtonColors.DISABLED_BG.value @@ -160,8 +164,6 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): tinted_icon_pixmap = QtGui.QPixmap(_icon_scaled.size()) tinted_icon_pixmap.fill(QtCore.Qt.GlobalColor.transparent) - margin = _style.pixelMetric(_style.PixelMetric.PM_ButtonMargin, opt, self) - if not self.isEnabled(): tinted_icon_pixmap = QtGui.QPixmap(_icon_scaled.size()) tinted_icon_pixmap.fill(QtCore.Qt.GlobalColor.transparent) @@ -190,24 +192,16 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): destination_point = adjusted_icon_rect.toRect().topLeft() painter.drawPixmap(destination_point, final_pixmap) - if self.text(): font_metrics = self.fontMetrics() self.text_width = font_metrics.horizontalAdvance(self._text) self.label_width = self.contentsRect().width() - margin = _style.pixelMetric( - _style.PixelMetric.PM_ButtonMargin, opt, self - ) - - _start_text_position = int(self.button_ellipse.width()) + # _start_text_position = int(self.button_ellipse.width()) _text_rect = _rect - _text_rect2 = _rect - _text_rect2.setWidth( - self.width() - int(self.button_ellipse.width()) - ) + _text_rect2.setWidth(self.width() - int(self.button_ellipse.width())) _text_rect2.setLeft(int(self.button_ellipse.width())) _text_rect.setWidth(self.width() - int(self.button_ellipse.width())) @@ -218,13 +212,10 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): _pen.setColor(current_text_color) painter.setPen(_pen) - # if self.text_width < _text_rect2.width()*0.6: - _text_rect.setWidth( - self.width() - int(self.button_ellipse.width()*1.4) - ) + _text_rect.setWidth(self.width() - int(self.button_ellipse.width() * 1.4)) _text_rect.setLeft(int(self.button_ellipse.width())) - + painter.drawText( _text_rect, QtCore.Qt.TextFlag.TextShowMnemonic @@ -257,6 +248,7 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): painter.end() def setProperty(self, name: str, value: typing.Any): + """Set widget properties""" if name == "icon_pixmap": self.icon_pixmap = value elif name == "name": @@ -265,14 +257,8 @@ def setProperty(self, name: str, value: typing.Any): self.text_color = QtGui.QColor(value) self.update() - def handleTouchBegin(self, e: QtCore.QEvent): ... - def handleTouchUpdate(self, e: QtCore.QEvent): ... - def handleTouchEnd(self, e: QtCore.QEvent): ... - def handleTouchCancel(self, e: QtCore.QEvent): ... - def setAutoDefault(self, bool): ... - def setFlat(self, bool): ... - def event(self, e: QtCore.QEvent) -> bool: + """Re-implemented method, filter events""" if e.type() == QtCore.QEvent.Type.TouchBegin: self.handleTouchBegin(e) return False diff --git a/BlocksScreen/lib/utils/blocks_frame.py b/BlocksScreen/lib/utils/blocks_frame.py index 05810d8c..6783ad8a 100644 --- a/BlocksScreen/lib/utils/blocks_frame.py +++ b/BlocksScreen/lib/utils/blocks_frame.py @@ -9,13 +9,16 @@ def __init__(self, parent=None): self._radius = 20 def setRadius(self, radius: int): + """Set widget frame radius""" self._radius = radius self.update() def radius(self): + """Get widget frame radius""" return self._radius - def paintEvent(self, event): + def paintEvent(self, event): + """Re-implemented method, paint widget""" painter = QPainter(self) painter.setRenderHint(QPainter.RenderHint.Antialiasing) rect = QRectF(self.rect()) @@ -23,6 +26,4 @@ def paintEvent(self, event): pen.setWidth(2) painter.setPen(pen) painter.setBrush(QBrush(QColor(50, 50, 50, 100))) - painter.drawRoundedRect( - rect.adjusted(1, 1, -1, -1), self._radius, self._radius - ) + painter.drawRoundedRect(rect.adjusted(1, 1, -1, -1), self._radius, self._radius) diff --git a/BlocksScreen/lib/utils/blocks_label.py b/BlocksScreen/lib/utils/blocks_label.py index 4df5a045..30529904 100644 --- a/BlocksScreen/lib/utils/blocks_label.py +++ b/BlocksScreen/lib/utils/blocks_label.py @@ -4,7 +4,7 @@ class BlocksLabel(QtWidgets.QLabel): def __init__(self, parent: QtWidgets.QWidget = None, *args, **kwargs): - super(BlocksLabel, self).__init__(parent, *args, **kwargs) + super().__init__(parent, *args, **kwargs) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) self.icon_pixmap: typing.Optional[QtGui.QPixmap] = None @@ -15,15 +15,13 @@ def __init__(self, parent: QtWidgets.QWidget = None, *args, **kwargs): self._marquee: bool = True self.timer = QtCore.QTimer() self.timer.timeout.connect(self._scroll_text) - self.delay_timer = QtCore.QTimer() - self.delay_timer.setSingleShot(True) - self.delay_timer.timeout.connect(self._start_marquee) - self.scroll_pos = 0.0 - self.marquee_spacing = 20 + self.marquee_spacing = 40 + self.scroll_speed = 40 + self.scroll_animation_speed = 30 + self.max_loops = 2 + self.loop_count = 0 self.paused = False - self.scroll_speed = 20 - self.scroll_animation_speed = 50 self.setMouseTracking(True) self.setTabletTracking(True) self.setSizePolicy( @@ -41,15 +39,19 @@ def __init__(self, parent: QtWidgets.QWidget = None, *args, **kwargs): self.glow_animation.finished.connect(self.repaint) self.total_scroll_width: float = 0.0 - self.marquee_delay = 5000 - self.loop_count = 0 + self.text_width: float = 0.0 + self.label_width: float = 0.0 + self.text_width: float = 0.0 + self.label_width: float = 0.0 self.first_run = True def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: + """Re-implemented method, handle widget resize event""" self.update_text_metrics() return super().resizeEvent(a0) def mousePressEvent(self, ev: QtGui.QMouseEvent) -> None: + """Re-implemented method, handle mouse press event""" if ( ev.button() == QtCore.Qt.MouseButton.LeftButton and not self.timer.isActive() @@ -58,15 +60,19 @@ def mousePressEvent(self, ev: QtGui.QMouseEvent) -> None: self.start_scroll() def setPixmap(self, a0: QtGui.QPixmap) -> None: + """Set widget pixmap""" self.icon_pixmap = a0 self.update() def setText(self, text: str) -> None: + """Set widget text""" self._text = text + self.scroll_pos = 0.0 self.update_text_metrics() @property def background_color(self) -> typing.Optional[QtGui.QColor]: + """Widget background color""" return self._background_color @background_color.setter @@ -75,6 +81,7 @@ def background_color(self, color: QtGui.QColor) -> None: @property def border_color(self) -> typing.Optional[QtGui.QColor]: + """Widget border color""" return self._border_color @border_color.setter @@ -83,6 +90,7 @@ def border_color(self, color: QtGui.QColor) -> None: @property def rounded(self) -> bool: + """Widget rounded property""" return self._rounded @rounded.setter @@ -91,15 +99,17 @@ def rounded(self, on: bool) -> None: @property def marquee(self) -> bool: + """Widget enable marquee effect""" return self._marquee @marquee.setter - def marquee(self, activate) -> None: + def marquee(self, activate: bool) -> None: self._marquee = activate self.update_text_metrics() @QtCore.pyqtProperty(int) def animation_speed(self) -> int: + """Widget animation speed property""" return self._animation_speed @animation_speed.setter @@ -108,6 +118,7 @@ def animation_speed(self, new_speed: int) -> None: @QtCore.pyqtProperty(QtGui.QColor) def glow_color(self) -> QtGui.QColor: + """Widget glow color property""" return self._glow_color @glow_color.setter @@ -117,18 +128,20 @@ def glow_color(self, color: QtGui.QColor) -> None: @QtCore.pyqtSlot(name="start_glow_animation") def start_glow_animation(self) -> None: + """Start glow animation""" self.glow_animation.setDuration(self.animation_speed) start_color = QtGui.QColor("#00000000") + end_color = QtGui.QColor("#E95757") + end_color = QtGui.QColor("#E95757") self.glow_animation.setStartValue(start_color) - base_end_color = QtGui.QColor("#E95757") - self.glow_animation.setEndValue(base_end_color) - + self.glow_animation.setEndValue(end_color) self.glow_animation.setDirection(QtCore.QPropertyAnimation.Direction.Forward) self.glow_animation.setLoopCount(-1) self.glow_animation.start() @QtCore.pyqtSlot(name="change_glow_direction") def change_glow_direction(self) -> None: + """Handle Change glow direction""" current_direction = self.glow_animation.direction() if current_direction == self.glow_animation.Direction.Forward: self.glow_animation.setDirection(self.glow_animation.Direction.Backward) @@ -136,39 +149,35 @@ def change_glow_direction(self) -> None: self.glow_animation.setDirection(self.glow_animation.Direction.Forward) def update_text_metrics(self) -> None: + """Recalculate text width and control marquee start/stop behavior.""" font_metrics = self.fontMetrics() self.text_width = font_metrics.horizontalAdvance(self._text) self.label_width = self.contentsRect().width() self.total_scroll_width = float(self.text_width + self.marquee_spacing) if self._marquee and self.text_width > self.label_width: - self.start_scroll() + self.scroll_pos = 0.0 + QtCore.QTimer.singleShot(2000, lambda: self.start_scroll()) else: self.stop_scroll() self.scroll_pos = 0.0 self.update() def start_scroll(self) -> None: - if not self.delay_timer.isActive() and not self.timer.isActive(): + """Start or restart the scrolling.""" + if not self.timer.isActive(): self.scroll_pos = 0 self.loop_count = 0 - if self.first_run: - self.delay_timer.start(self.marquee_delay) - self.first_run = False - else: - self._start_marquee() - - def _start_marquee(self) -> None: - """Starts the actual marquee animation after the delay or immediately.""" - if not self.timer.isActive(): self.timer.start(self.scroll_animation_speed) def stop_scroll(self) -> None: + """Stop all marquee timers.""" self.timer.stop() - self.delay_timer.stop() + self.repaint() def _scroll_text(self) -> None: - if self.paused: + """Smoothly scroll the text leftwards.""" + if not self._marquee or self.paused: return p_to_m = self.scroll_speed * (self.scroll_animation_speed / 1000.0) @@ -176,120 +185,104 @@ def _scroll_text(self) -> None: if self.scroll_pos <= -self.total_scroll_width: self.loop_count += 1 - if self.loop_count >= 2: + if self.loop_count >= self.max_loops: self.stop_scroll() + self.scroll_pos = 0 + return else: self.scroll_pos = 0 - self.repaint() - + self.update() def paintEvent(self, a0: QtGui.QPaintEvent) -> None: - qp = QtWidgets.QStylePainter(self) - opt = QtWidgets.QStyleOption() - opt.initFrom(self) - + """Re-implemented method, paint widget""" + qp = QtGui.QPainter(self) qp.setRenderHint(qp.RenderHint.Antialiasing, True) qp.setRenderHint(qp.RenderHint.SmoothPixmapTransform, True) qp.setRenderHint(qp.RenderHint.LosslessImageRendering, True) - _rect = self.rect() - _style = self.style() - - icon_margin = _style.pixelMetric(_style.PixelMetric.PM_HeaderMargin, opt, self) - if not _style or _rect.isNull(): - return + rect = self.contentsRect() + if self._background_color: + qp.setBrush(self._background_color) + qp.setPen(QtCore.Qt.PenStyle.NoPen) + if self._rounded: + path = QtGui.QPainterPath() + path.addRoundedRect(QtCore.QRectF(rect), 10, 10) + path.addRoundedRect(QtCore.QRectF(rect), 10, 10) + qp.fillPath(path, self._background_color) + else: + qp.fillRect(rect, self._background_color) if self.icon_pixmap: - qp.setCompositionMode(qp.CompositionMode.CompositionMode_SourceOver) - _icon_rect = QtCore.QRectF( - 0.0 + icon_margin, - 0.0 + icon_margin, - self.width() - icon_margin, - self.height() - icon_margin, - ) - _icon_scaled = self.icon_pixmap.scaled( - _icon_rect.size().toSize(), + icon_rect = QtCore.QRectF(0, 0, self.height(), self.height()) + scaled = self.icon_pixmap.scaled( + icon_rect.size().toSize(), QtCore.Qt.AspectRatioMode.KeepAspectRatio, QtCore.Qt.TransformationMode.SmoothTransformation, ) - scaled_width = _icon_scaled.width() - scaled_height = _icon_scaled.height() - adjusted_x = (_icon_rect.width() - scaled_width) / 2.0 - adjusted_y = (_icon_rect.height() - scaled_height) / 2.0 - adjusted_icon_rect = QtCore.QRectF( - _icon_rect.x() + adjusted_x, - _icon_rect.y() + adjusted_y, - scaled_width, - scaled_height, - ) - qp.drawPixmap( - adjusted_icon_rect, _icon_scaled, _icon_scaled.rect().toRectF() - ) - - big_rect = QtGui.QPainterPath() - rect = self.contentsRect().toRectF() - big_rect.addRoundedRect(rect, 10.0, 10.0, QtCore.Qt.SizeMode.AbsoluteSize) - mini_rect = QtCore.QRectF( - (rect.width() - rect.width() * 0.99) / 2, - (rect.height() - rect.height() * 0.85) / 2, - rect.width() * 0.99, - rect.height() * 0.85, - ) - mini_path = QtGui.QPainterPath() - mini_path.addRoundedRect(mini_rect, 10.0, 10.0, QtCore.Qt.SizeMode.AbsoluteSize) - subtracted = big_rect.subtracted(mini_path) - + qp.drawPixmap(icon_rect.toRect(), scaled) if self.glow_animation.state() == self.glow_animation.State.Running: - qp.setCompositionMode(qp.CompositionMode.CompositionMode_SourceAtop) - subtracted.setFillRule(QtCore.Qt.FillRule.OddEvenFill) - qp.fillPath(subtracted, self.glow_color) + path = QtGui.QPainterPath() + path.addRoundedRect(QtCore.QRectF(rect), 10, 10) + path.addRoundedRect(QtCore.QRectF(rect), 10, 10) + qp.fillPath(path, self.glow_color) if self._text: - qp.setCompositionMode(qp.CompositionMode.CompositionMode_SourceOver) - - text_rect = self.contentsRect() - text_rect.translate(int(self.scroll_pos), 0) - text_path = QtGui.QPainterPath() - text_path.addRect(self.contentsRect().toRectF()) - qp.setClipPath(text_path) - text_option = QtGui.QTextOption(self.alignment()) text_option.setWrapMode(QtGui.QTextOption.WrapMode.NoWrap) - qp.drawText( - QtCore.QRectF(text_rect), - self._text, - text_option, + qp.save() + qp.setClipRect(rect) + baseline_y = ( + rect.y() + + (rect.height() + self.fontMetrics().ascent() - self.fontMetrics().descent()) + / 2 ) - if self._marquee and self.text_width > self.label_width: - second_text_rect = self.rect() - second_text_rect.translate( - int(self.scroll_pos + self.text_width + self.label_width / 2), - 0, + + if self.text_width > self.label_width: + qp.drawText( + QtCore.QPointF(rect.x() + self.scroll_pos, baseline_y), self._text + ) + # Draw scrolling repeater text + qp.drawText( + QtCore.QPointF( + rect.x() + self.scroll_pos + self.total_scroll_width, baseline_y + ), + self._text, ) - # Draw the main text instance - draw_rect = QtCore.QRectF( - self.contentsRect().x() + self.scroll_pos, - self.contentsRect().y(), - self.text_width, - self.contentsRect().height(), + else: + center_x = rect.x() + (rect.width() - self.text_width) / 2 + + qp.drawText( + QtCore.QPointF(center_x, baseline_y), + self._text + ) + baseline_y = ( + rect.y() + + (rect.height() + self.fontMetrics().ascent() - self.fontMetrics().descent()) + / 2 + ) + + if self.text_width > self.label_width: + qp.drawText( + QtCore.QPointF(rect.x() + self.scroll_pos, baseline_y), self._text ) - qp.drawText(QtCore.QRectF(second_text_rect), self._text, text_option) - - draw_rect2 = QtCore.QRectF( - self.contentsRect().x() - + self.scroll_pos - + self.text_width - + self.marquee_spacing, - self.contentsRect().y(), - self.text_width, - self.contentsRect().height(), + # Draw scrolling repeater text + qp.drawText( + QtCore.QPointF( + rect.x() + self.scroll_pos + self.total_scroll_width, baseline_y + ), + self._text, ) - qp.drawText(draw_rect2, self._text, text_option) else: - text_rect = self.contentsRect().toRectF() - qp.drawText(text_rect, self._text, text_option) + center_x = rect.x() + (rect.width() - self.text_width) / 2 + + qp.drawText( + QtCore.QPointF(center_x, baseline_y), + self._text + ) + qp.restore() qp.end() def setProperty(self, name: str, value: typing.Any) -> bool: + """Re-implemented method, set widget properties""" if name == "icon_pixmap": self.setPixmap(value) - return super().setProperty(name, value) + return super().setProperty(name, value) \ No newline at end of file diff --git a/BlocksScreen/lib/utils/blocks_linedit.py b/BlocksScreen/lib/utils/blocks_linedit.py index c40cabfb..242e4b0d 100644 --- a/BlocksScreen/lib/utils/blocks_linedit.py +++ b/BlocksScreen/lib/utils/blocks_linedit.py @@ -3,7 +3,7 @@ class BlocksCustomLinEdit(QtWidgets.QLineEdit): - clicked = QtCore.pyqtSignal() + clicked = QtCore.pyqtSignal() def __init__( self, @@ -17,11 +17,12 @@ def __init__( self.placeholder_str = "Type here" self._name: str = "" self.text_color: QtGui.QColor = QtGui.QColor(0, 0, 0) - self.secret: bool = False + self.secret: bool = False self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) @property def name(self): + """Widget name""" return self._name @name.setter @@ -30,18 +31,21 @@ def name(self, new_name) -> None: self.setObjectName(new_name) def setText(self, text: str) -> None: + """Set widget text""" super().setText(text) def setHidden(self, hidden: bool) -> None: + """Hide widget text""" self.secret = hidden self.update() def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: - self.clicked.emit() - super().mousePressEvent(event) - + """Re-implemented method, handle mouse press events""" + self.clicked.emit() + super().mousePressEvent(event) def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(painter.RenderHint.Antialiasing, True) @@ -51,7 +55,7 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): painter.setPen(QtCore.Qt.PenStyle.NoPen) painter.drawRoundedRect(self.rect(), 8, 8) - margin = 5 + margin = 5 display_text = self.text() if self.secret and display_text: display_text = "*" * len(display_text) diff --git a/BlocksScreen/lib/utils/blocks_progressbar.py b/BlocksScreen/lib/utils/blocks_progressbar.py index 7688bf82..98f7cc52 100644 --- a/BlocksScreen/lib/utils/blocks_progressbar.py +++ b/BlocksScreen/lib/utils/blocks_progressbar.py @@ -1,4 +1,4 @@ -from PyQt6 import QtWidgets ,QtGui ,QtCore +from PyQt6 import QtWidgets, QtGui, QtCore class CustomProgressBar(QtWidgets.QProgressBar): @@ -11,14 +11,17 @@ def __init__(self, parent=None): self.set_pen_width(20) def set_padding(self, value): + """Set widget padding""" self.padding = value self.update() def set_pen_width(self, value): + """Set widget text pen width""" self.pen_width = value self.update() def paintEvent(self, event): + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing) @@ -30,9 +33,8 @@ def _draw_circular_bar(self, painter, width, height): y = (height - size) / 2 arc_rect = QtCore.QRectF(x, y, size, size) - - arc1_start = 236* 16 - arc1_span = -290 * 16 + arc1_start = 236 * 16 + arc1_span = -290 * 16 bg_pen = QtGui.QPen(QtGui.QColor(20, 20, 20)) bg_pen.setWidth(self.pen_width) bg_pen.setCapStyle(QtCore.Qt.PenCapStyle.RoundCap) @@ -40,7 +42,7 @@ def _draw_circular_bar(self, painter, width, height): painter.drawArc(arc_rect, arc1_start, arc1_span) if self.progress_value is not None: - gradient = QtGui.QConicalGradient(arc_rect.center(), -90) + gradient = QtGui.QConicalGradient(arc_rect.center(), -90) gradient.setColorAt(0.0, self.bar_color) gradient.setColorAt(1.0, QtGui.QColor(100, 100, 100)) @@ -51,7 +53,7 @@ def _draw_circular_bar(self, painter, width, height): painter.setPen(progress_pen) # scale only over arc1’s span - progress_span = int(arc1_span * self.progress_value/100) + progress_span = int(arc1_span * self.progress_value / 100) painter.drawArc(arc_rect, arc1_start, progress_span) progress_text = f"{int(self.progress_value)}%" @@ -67,14 +69,14 @@ def _draw_circular_bar(self, painter, width, height): text_y = arc_rect.center().y() # Draw centered text - text_rect = QtCore.QRectF(text_x - 30, text_y + arc_rect.height() / 2 - 25, 60, 40) + text_rect = QtCore.QRectF( + text_x - 30, text_y + arc_rect.height() / 2 - 25, 60, 40 + ) painter.drawText(text_rect, QtCore.Qt.AlignmentFlag.AlignCenter, progress_text) - - - def setValue(self, value): - value*=100 + """Set value""" + value *= 100 if 0 <= value <= 101: self.progress_value = value self.update() @@ -82,6 +84,7 @@ def setValue(self, value): raise ValueError("Progress must be between 0.0 and 1.0.") def set_bar_color(self, red, green, blue): + """Set bar color""" if 0 <= red <= 255 and 0 <= green <= 255 and 0 <= blue <= 255: self.bar_color = QtGui.QColor(red, green, blue) self.update() diff --git a/BlocksScreen/lib/utils/blocks_slider.py b/BlocksScreen/lib/utils/blocks_slider.py index 4d1078c7..ee084a0a 100644 --- a/BlocksScreen/lib/utils/blocks_slider.py +++ b/BlocksScreen/lib/utils/blocks_slider.py @@ -1,5 +1,3 @@ -import sys - from PyQt6 import QtCore, QtGui, QtWidgets @@ -17,11 +15,8 @@ def __init__(self, parent) -> None: self.setMinimum(0) self.setMaximum(100) - def setOrientation(self, a0: QtCore.Qt.Orientation) -> None: - return super().setOrientation(a0) - def mousePressEvent(self, ev: QtGui.QMouseEvent) -> None: - """Handle mouse press events""" + """Re-implemented method, Handle mouse press events""" if (ev.button() == QtCore.Qt.MouseButton.LeftButton) and self.hit_test( ev.position().toPoint().toPointF() ): @@ -69,31 +64,28 @@ def _set_slider_pos(self, pos: QtCore.QPointF): slider_start = self._groove_rect.x() pos_x = pos.x() new_val = ( - min_val - + (max_val - min_val) * (pos_x - slider_start) // slider_length + min_val + (max_val - min_val) * (pos_x - slider_start) // slider_length ) else: slider_length = self._groove_rect.height() slider_start = self._groove_rect.y() pos_y = pos.y() new_val = ( - min_val - + (max_val - min_val) * (pos_y - slider_start) / slider_length + min_val + (max_val - min_val) * (pos_y - slider_start) / slider_length ) self.setSliderPosition(int(round(new_val))) self.setValue(int(round(new_val))) self.update() def paintEvent(self, ev: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" opt = QtWidgets.QStyleOptionSlider() self.initStyleOption(opt) _style = self.style() # Clip the opt rect inside, so the handle and # groove doesn't exceed the limits - opt.rect = opt.rect.adjusted( - 12, 10, -18, 20 - ) # This is a bit hardcoded + opt.rect = opt.rect.adjusted(12, 10, -18, 20) # This is a bit hardcoded self._groove_rect = _style.subControlRect( QtWidgets.QStyle.ComplexControl.CC_Slider, @@ -162,9 +154,7 @@ def paintEvent(self, ev: QtGui.QPaintEvent) -> None: painter.setRenderHint(painter.RenderHint.TextAntialiasing, True) _color = QtGui.QColor(164, 164, 164) _color.setAlphaF(0.5) - painter.fillPath( - _groove_path, _color - ) # Primary groove background color + painter.fillPath(_groove_path, _color) # Primary groove background color _color = QtGui.QColor(self.highlight_color) _color_1 = QtGui.QColor(self.highlight_color) @@ -187,7 +177,6 @@ def paintEvent(self, ev: QtGui.QPaintEvent) -> None: QtWidgets.QStyle.SubControl.SC_SliderTickmarks, self, ) - tick_interval = self.tickInterval() or self.singleStep() min_v, max_v = self.minimum(), self.maximum() painter.setPen(QtGui.QColor("#888888")) fm = QtGui.QFontMetrics(painter.font()) diff --git a/BlocksScreen/lib/utils/blocks_tabwidget.py b/BlocksScreen/lib/utils/blocks_tabwidget.py index 274607a2..4696d967 100644 --- a/BlocksScreen/lib/utils/blocks_tabwidget.py +++ b/BlocksScreen/lib/utils/blocks_tabwidget.py @@ -2,17 +2,21 @@ class NotificationTabBar(QtWidgets.QTabBar): + """Re-implemented QTabBar so that the widget can have notifications""" + def __init__(self, parent=None): super().__init__(parent) self._notifications = {} # {tab_index: bool} def setNotification(self, index: int, show: bool): + """Set notification""" if index < 0 or index >= self.count(): return self._notifications[index] = show self.update(self.tabRect(index)) # repaint only that tab def paintEvent(self, event): + """Re-implemented method, paint widget""" super().paintEvent(event) painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing) @@ -31,10 +35,13 @@ def paintEvent(self, event): class NotificationQTabWidget(QtWidgets.QTabWidget): + """Re-implemented QTabWidget so that we can have notifications""" + def __init__(self, parent=None): super().__init__(parent) self._custom_tabbar = NotificationTabBar() self.setTabBar(self._custom_tabbar) def setNotification(self, index: int, show: bool): + """Set tab notification""" self._custom_tabbar.setNotification(index, show) diff --git a/BlocksScreen/lib/utils/blocks_togglebutton.py b/BlocksScreen/lib/utils/blocks_togglebutton.py index 4363b655..c97e8f1f 100644 --- a/BlocksScreen/lib/utils/blocks_togglebutton.py +++ b/BlocksScreen/lib/utils/blocks_togglebutton.py @@ -12,39 +12,41 @@ def __init__(self, parent): self.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight) self._icon_label = None self._text_label = None - self._text: str = ("la test") + self._text: str = "la test" self.icon_pixmap_fp: QtGui.QPixmap = QtGui.QPixmap( ":/filament_related/media/btn_icons/filament_sensor_turn_on.svg" ) - - self.setupUI() + + self._setupUI() self.tb = self.toggle_button def text(self) -> str: + """Button text""" return self._text def setText(self, new_text) -> None: + """Set widget text""" if self._text_label is not None: self._text_label.setText(f"{new_text}") self._text = new_text - - def setPixmap(self,pixmap: QtGui.QPixmap): + def setPixmap(self, pixmap: QtGui.QPixmap): + """Set widget pixmap""" self.icon_pixmap_fp = pixmap def mousePressEvent(self, event: QtGui.QMouseEvent): + """Re-implemented method, handle mouse press events""" if self.toggle_button.geometry().contains(event.pos()): event.ignore() return if event.button() == QtCore.Qt.MouseButton.LeftButton: self.clicked.emit() - event.accept() + event.accept() def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" style_painter = QtWidgets.QStylePainter(self) - style_painter.setRenderHint( - style_painter.RenderHint.Antialiasing, True - ) + style_painter.setRenderHint(style_painter.RenderHint.Antialiasing, True) style_painter.setRenderHint( style_painter.RenderHint.SmoothPixmapTransform, True ) @@ -76,12 +78,13 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: style_painter.end() def setDisabled(self, a0: bool) -> None: + """Re-implemented method, disable widget""" self.toggle_button.setDisabled(a0) self.repaint() self.toggle_button.repaint() return super().setDisabled(a0) - def setupUI(self): + def _setupUI(self): _policy = QtWidgets.QSizePolicy.Policy.MinimumExpanding size_policy = QtWidgets.QSizePolicy(_policy, _policy) size_policy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) @@ -90,29 +93,21 @@ def setupUI(self): self.sensor_horizontal_layout.setGeometry(self.rect()) self.sensor_horizontal_layout.setObjectName("sensorHorizontalLayout") self._icon_label = BlocksLabel(self) - size_policy.setHeightForWidth( - self._icon_label.sizePolicy().hasHeightForWidth() - ) + size_policy.setHeightForWidth(self._icon_label.sizePolicy().hasHeightForWidth()) self._icon_label.setSizePolicy(size_policy) self._icon_label.setMinimumSize(60, 60) self._icon_label.setMaximumSize(60, 60) - self._icon_label.setPixmap( - self.icon_pixmap_fp - ) + self._icon_label.setPixmap(self.icon_pixmap_fp) self.sensor_horizontal_layout.addWidget(self._icon_label) self._text_label = QtWidgets.QLabel(parent=self) - size_policy.setHeightForWidth( - self._text_label.sizePolicy().hasHeightForWidth() - ) + size_policy.setHeightForWidth(self._text_label.sizePolicy().hasHeightForWidth()) self._text_label.setMinimumSize(100, 60) self._text_label.setMaximumSize(500, 60) _font = QtGui.QFont() _font.setStyleStrategy(QtGui.QFont.StyleStrategy.PreferAntialias) _font.setPointSize(18) palette = self._text_label.palette() - palette.setColor( - palette.ColorRole.WindowText, QtGui.QColorConstants.White - ) + palette.setColor(palette.ColorRole.WindowText, QtGui.QColorConstants.White) self._text_label.setPalette(palette) self._text_label.setFont(_font) self._text_label.setText(str(self._text)) diff --git a/BlocksScreen/lib/utils/display_button.py b/BlocksScreen/lib/utils/display_button.py index a0e5a886..1f798dfe 100644 --- a/BlocksScreen/lib/utils/display_button.py +++ b/BlocksScreen/lib/utils/display_button.py @@ -4,9 +4,7 @@ class DisplayButton(QtWidgets.QPushButton): - def __init__( - self, parent: typing.Optional["QtWidgets.QWidget"] = None - ) -> None: + def __init__(self, parent: typing.Optional["QtWidgets.QWidget"] = None) -> None: if parent: super().__init__(parent=parent) else: @@ -19,22 +17,23 @@ def __init__( self._text: str = "" self._secondary_text: str = "" self._name: str = "" - self.display_format: typing.Literal["normal", "upper_downer"] = ( - "normal" - ) + self.display_format: typing.Literal["normal", "upper_downer"] = "normal" self.text_color: QtGui.QColor = QtGui.QColor(0, 0, 0) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) @property def name(self): + """Widget name""" return self._name def setPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set widget pixmap""" self.icon_pixmap = pixmap self.repaint() @property def button_type(self) -> str: + """Widget button type""" return self._button_type @button_type.setter @@ -44,29 +43,28 @@ def button_type(self, type) -> None: self._button_type = type def text(self) -> str: + """Widget text""" return self._text def setText(self, text: str) -> None: + """Set widget text""" self._text = text self.update() super().setText(text) @property def secondary_text(self) -> str: + """Widget secondary text""" return self._secondary_text @secondary_text.setter def secondary_text(self, text: str) -> None: + """Set secondary text""" self._secondary_text = text self.update() - def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: - return super().resizeEvent(a0) - - def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: - return super().mousePressEvent(e) - def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" opt = QtWidgets.QStyleOptionButton() self.initStyleOption(opt) painter = QtWidgets.QStylePainter(self) @@ -78,9 +76,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: if not _style or _rect is None: return - margin = _style.pixelMetric( - _style.PixelMetric.PM_ButtonMargin, opt, self - ) + margin = _style.pixelMetric(_style.PixelMetric.PM_ButtonMargin, opt, self) # Rounded background edges path = QtGui.QPainterPath() path.addRoundedRect( @@ -119,13 +115,11 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: _pen.setBrush(_gradient) painter.fillPath(path, _pen.brush()) - _icon_rect = ( - QtCore.QRectF( # x,y, width * size reduction factor, height - 0.0, - 0.0, - (_rect.width() * 0.3) - 5.0, - _rect.height() - 5, - ) + _icon_rect = QtCore.QRectF( # x,y, width * size reduction factor, height + 0.0, + 0.0, + (_rect.width() * 0.3) - 5.0, + _rect.height() - 5, ) _icon_scaled = self.icon_pixmap.scaled( @@ -192,9 +186,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: QtCore.Qt.TextFlag.TextShowMnemonic | QtCore.Qt.AlignmentFlag.AlignHCenter | QtCore.Qt.AlignmentFlag.AlignVCenter, - str(self.secondary_text) - if self.secondary_text - else str("?"), + str(self.secondary_text) if self.secondary_text else str("?"), ) painter.drawText( _mtl_rect, @@ -205,9 +197,9 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: ) elif self.display_format == "upper_downer": _mtl = QtCore.QRectF( - int(_icon_rect.width()) + margin , + int(_icon_rect.width()) + margin, 0.0, - int(_rect.width() - _icon_rect.width() - margin ), + int(_rect.width() - _icon_rect.width() - margin), _rect.height(), ) _upper_rect = QtCore.QRectF( @@ -226,7 +218,9 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: font.setPointSize(20) font.setFamily("Momcake-bold") painter.setFont(font) - painter.setCompositionMode(painter.CompositionMode.CompositionMode_SourceAtop) + painter.setCompositionMode( + painter.CompositionMode.CompositionMode_SourceAtop + ) painter.drawText( _upper_rect, # QtCore.Qt.AlignmentFlag.AlignCenter, @@ -237,7 +231,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: font.setPointSize(15) painter.setPen(QtGui.QColor("#b6b0b0")) painter.setFont(font) - + painter.drawText( _downer_rect, QtCore.Qt.AlignmentFlag.AlignRight @@ -258,6 +252,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: return def setProperty(self, name: str, value: typing.Any) -> bool: + """Re-implemented method, set widget properties""" if name == "icon_pixmap": self.icon_pixmap = value elif name == "button_type": diff --git a/BlocksScreen/lib/utils/group_button.py b/BlocksScreen/lib/utils/group_button.py index b9af752b..79f4251e 100644 --- a/BlocksScreen/lib/utils/group_button.py +++ b/BlocksScreen/lib/utils/group_button.py @@ -27,6 +27,7 @@ def __init__( @property def name(self): + """Widget name""" return self._name @name.setter @@ -35,18 +36,22 @@ def name(self, new_name) -> None: self.setObjectName(new_name) def text(self) -> str | None: + """Widget text""" return self._text def setText(self, text: str) -> None: + """Set widget text""" self._text = text self.update() # Force button update return def setPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set widget pixmap""" self.icon_pixmap = pixmap self.repaint() def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): + """Re-implemented method, paint widget""" opt = QtWidgets.QStyleOptionButton() self.initStyleOption(opt) @@ -117,28 +122,15 @@ def paintEvent(self, e: typing.Optional[QtGui.QPaintEvent]): painter.setPen(QtCore.Qt.PenStyle.NoPen) def setProperty(self, name: str, value: typing.Any): + """Re-implemented method, set widget properties""" if name == "name": self._name = name elif name == "text_color": self.text_color = QtGui.QColor(value) # return super().setProperty(name, value) - def handleTouchBegin(self, e: QtCore.QEvent): - ... - # if not self.button_background: - # if self.button_background.contains(e.pos()): # type: ignore - # # super().mousePressEvent(e) - # self.mousePressEvent(e) # type: ignore - # return - # else: - # e.ignore() - # return - - def handleTouchUpdate(self, e: QtCore.QEvent): ... - def handleTouchEnd(self, e: QtCore.QEvent): ... - def handleTouchCancel(self, e: QtCore.QEvent): ... - def event(self, e: QtCore.QEvent) -> bool: + """Re-implemented method, filter events""" if e.type() == QtCore.QEvent.Type.TouchBegin: self.handleTouchBegin(e) return False diff --git a/BlocksScreen/lib/utils/icon_button.py b/BlocksScreen/lib/utils/icon_button.py index 019471b4..a60a7f1d 100644 --- a/BlocksScreen/lib/utils/icon_button.py +++ b/BlocksScreen/lib/utils/icon_button.py @@ -16,21 +16,25 @@ def __init__(self, parent: QtWidgets.QWidget) -> None: @property def name(self): + """Widget name""" return self._name def text(self) -> str: + """Widget text""" return self._text def setPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set widget pixmap""" self.icon_pixmap = pixmap self.repaint() def setText(self, text: str) -> None: + """Set widget text""" self._text = text self.update() - # super().setText(text) def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" opt = QtWidgets.QStyleOptionButton() self.initStyleOption(opt) painter = QtWidgets.QStylePainter(self) @@ -53,9 +57,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: # * Build icon x = y = 15.0 if self.text_formatting else 5.0 - _icon_rect = QtCore.QRectF( - 0.0, 0.0, (self.width() - x), (self.height() - y) - ) + _icon_rect = QtCore.QRectF(0.0, 0.0, (self.width() - x), (self.height() - y)) _icon_scaled = self.icon_pixmap.scaled( _icon_rect.size().toSize(), @@ -120,6 +122,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.end() def setProperty(self, name: str, value: typing.Any) -> bool: + """Re-implemented method, set widget properties""" if name == "icon_pixmap": self.icon_pixmap = value elif name == "text_formatting": diff --git a/BlocksScreen/lib/utils/list_button.py b/BlocksScreen/lib/utils/list_button.py index 0b7eab6c..deb01bf1 100644 --- a/BlocksScreen/lib/utils/list_button.py +++ b/BlocksScreen/lib/utils/list_button.py @@ -29,56 +29,66 @@ def __init__(self, parent=None) -> None: self.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) def setText(self, text: str) -> None: + """Set widget text""" self._text = text self.update() def text(self) -> str: + """Widget text""" return self._text def setRightText(self, text: str) -> None: + """Set widget right text""" self._right_text = text self.update() def rightText(self) -> str: + """Widget right text""" return self._right_text def setLeftFontSize(self, size: int) -> None: + """Set widget left text font size""" self._lfontsize = size self.update() def setRightFontSize(self, size: int) -> None: + """Set widget right text font size""" self._rfontsize = size self.update() def setPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set widget pixmap""" self.icon_pixmap = pixmap self.update() def setSecondPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set widget secondary pixmap""" self.second_icon_pixmap = pixmap self.update() def mousePressEvent(self, event: QtGui.QMouseEvent) -> None: + """Re-implemented method, handle mouse press event""" self._is_pressed = True self.update() super().mousePressEvent(event) def mouseReleaseEvent(self, event: QtGui.QMouseEvent) -> None: + """Re-implemented method, handle mouse release event""" self._is_pressed = False self.update() super().mouseReleaseEvent(event) def leaveEvent(self, event: QtCore.QEvent) -> None: + """Re-implemented method, handle leave event""" self._is_hovered = False self.update() super().leaveEvent(event) def paintEvent(self, e: QtGui.QPaintEvent | None) -> None: + """Re-implemented method, paint widget""" painter = QtGui.QPainter(self) painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True) - painter.setRenderHint( - QtGui.QPainter.RenderHint.SmoothPixmapTransform, True - ) + painter.setRenderHint(QtGui.QPainter.RenderHint.SmoothPixmapTransform, True) rect = self.rect() radius = rect.height() / 5.0 @@ -140,12 +150,9 @@ def paintEvent(self, e: QtGui.QPaintEvent | None) -> None: QtCore.Qt.TransformationMode.SmoothTransformation, ) # Center the icon in the ellipse - adjusted_x = ( - icon_rect.x() + (icon_rect.width() - icon_scaled.width()) / 2.0 - ) + adjusted_x = icon_rect.x() + (icon_rect.width() - icon_scaled.width()) / 2.0 adjusted_y = ( - icon_rect.y() - + (icon_rect.height() - icon_scaled.height()) / 2.0 + icon_rect.y() + (icon_rect.height() - icon_scaled.height()) / 2.0 ) adjusted_icon_rect = QtCore.QRectF( adjusted_x, @@ -185,9 +192,7 @@ def paintEvent(self, e: QtGui.QPaintEvent | None) -> None: left_icon_scaled, left_icon_scaled.rect().toRectF(), ) - left_margin = ( - left_icon_margin + left_icon_size + 8 - ) # 8px gap after icon + left_margin = left_icon_margin + left_icon_size + 8 # 8px gap after icon # Draw text, area before the ellipse (adjusted for left icon) text_margin = int( @@ -209,11 +214,7 @@ def paintEvent(self, e: QtGui.QPaintEvent | None) -> None: main_text_height = metrics.height() # Vertically center text - text_y = ( - rect.top() - + (rect.height() + main_text_height) / 2 - - metrics.descent() - ) + text_y = rect.top() + (rect.height() + main_text_height) / 2 - metrics.descent() # Calculate where to start the right text: just left of the right icon ellipse gap = 10 # gap between right text and icon ellipse diff --git a/BlocksScreen/lib/utils/list_model.py b/BlocksScreen/lib/utils/list_model.py index a143f90b..24c9467f 100644 --- a/BlocksScreen/lib/utils/list_model.py +++ b/BlocksScreen/lib/utils/list_model.py @@ -6,7 +6,7 @@ @dataclass class ListItem: - """Data for a list item""" + """List item data""" text: str right_text: str = "" diff --git a/BlocksScreen/lib/utils/loadAnimatedLabel.py b/BlocksScreen/lib/utils/loadAnimatedLabel.py deleted file mode 100644 index 4e091b48..00000000 --- a/BlocksScreen/lib/utils/loadAnimatedLabel.py +++ /dev/null @@ -1,6 +0,0 @@ -from PyQt6 import QtGui, QtWidgets, QtCore - -class LoadAnimatedLabel(QtWidgets.QLabel): - def __init__(self, parent) -> None: - super().__init__(parent) - \ No newline at end of file diff --git a/BlocksScreen/lib/utils/numpad_button.py b/BlocksScreen/lib/utils/numpad_button.py index 5f0ebafe..feaf3c61 100644 --- a/BlocksScreen/lib/utils/numpad_button.py +++ b/BlocksScreen/lib/utils/numpad_button.py @@ -9,12 +9,15 @@ def __init__(self, parent=None): self._position: str = "" def get_position(self): + """Get numpad button position""" return self._position def set_position(self, value): + """Set position""" self._position = str(value).lower() def paintEvent(self, e: QtGui.QPaintEvent | None): + """Re-implemented method, paint widget""" opt = QtWidgets.QStyleOptionButton() self.initStyleOption(opt) @@ -28,9 +31,7 @@ def paintEvent(self, e: QtGui.QPaintEvent | None): if _style is None or _rect is None: return - margin = _style.pixelMetric( - _style.PixelMetric.PM_ButtonMargin, opt, self - ) + margin = _style.pixelMetric(_style.PixelMetric.PM_ButtonMargin, opt, self) bg_color = ( QtGui.QColor(164, 164, 164) if self.isDown() @@ -143,6 +144,7 @@ def paintEvent(self, e: QtGui.QPaintEvent | None): painter.setPen(QtCore.Qt.PenStyle.NoPen) def setProperty(self, name: str, value: typing.Any): + """Re-implemented method, set widget properties""" if name == "position": self.set_position(value) diff --git a/BlocksScreen/lib/utils/others.py b/BlocksScreen/lib/utils/others.py deleted file mode 100644 index f2b1e600..00000000 --- a/BlocksScreen/lib/utils/others.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python -import logging -import queue -import threading -import typing -from functools import partial - -from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot -from PyQt6.QtWidgets import QPushButton, QStackedWidget, QStyle, QWidget - -# from qt_ui.customNumpad_ui import Ui_customNumpad - -_logger = logging.getLogger(__name__) - - - - -# PYTHON 'is' checks if the object points to the same object, which is different than == - - - - - - -# TODO: Create a method that checks if the application requirements -# TODO: Create a method that validates the working directory of the GUI - - -def validate_requirements(): ... -def validate_working_dir(): ... -def scan_dir(dir: str) -> typing.Dict: ... -def scan_file(filename: str, dir: str) -> bool: ... - - -def validate_directory() -> bool: ... - - -def scan_directory(dir: str, ext: str = None): - ... - # """Scan a directory for files and nested directories""" - # if not isinstance(dir, str): - # raise ValueError("dir expected str type") - - # if os.access(dir, os.X_OK and os.W_OK and os.R_OK): - # for root, dirs, files in os.walk(dir): - - # for diff --git a/BlocksScreen/lib/utils/toggleAnimatedButton.py b/BlocksScreen/lib/utils/toggleAnimatedButton.py index 557c01a4..8cf90f10 100644 --- a/BlocksScreen/lib/utils/toggleAnimatedButton.py +++ b/BlocksScreen/lib/utils/toggleAnimatedButton.py @@ -16,9 +16,7 @@ def __init__(self, parent) -> None: super().__init__(parent) self.setMinimumSize(QtCore.QSize(80, 40)) self.setAttribute(QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, True) - self.setAttribute( - QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True - ) + self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TranslucentBackground, True) self.setMaximumHeight(80) self.setMouseTracking(True) @@ -49,17 +47,14 @@ def __init__(self, parent) -> None: else self._handle_OFFPosition ) - self.slide_animation = QtCore.QPropertyAnimation( - self, b"handle_position" - ) + self.slide_animation = QtCore.QPropertyAnimation(self, b"handle_position") self.slide_animation.setDuration(self._animation_speed) - self.slide_animation.setEasingCurve( - QtCore.QEasingCurve().Type.InOutQuart - ) + self.slide_animation.setEasingCurve(QtCore.QEasingCurve().Type.InOutQuart) self.pressed.connect(self.setup_animation) def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: + """Re-implemented method, handle widget resize event""" self.handle_radius = ( self.contentsRect().toRectF().normalized().height() * 0.80 ) // 2 @@ -74,10 +69,12 @@ def resizeEvent(self, a0: QtGui.QResizeEvent) -> None: return super().resizeEvent(a0) def sizeHint(self) -> QtCore.QSize: + """Re-implemented method, widget size hint""" return QtCore.QSize(80, 40) @QtCore.pyqtProperty(int) def animation_speed(self) -> int: + """Widget property animation speed""" return self._animation_speed @animation_speed.setter @@ -87,12 +84,13 @@ def animation_speed(self, new_speed: int) -> None: @property def state(self) -> State: + """Widget property, toggle state""" return self._state @state.setter def state(self, new_state: State) -> None: - if self._state == new_state: - return + if self._state == new_state: + return self._state = new_state if self.isVisible(): self.stateChange.emit(self._state) @@ -101,6 +99,7 @@ def state(self, new_state: State) -> None: @QtCore.pyqtProperty(float) def handle_position(self) -> float: + """Widget property handle position""" return self._handle_position @handle_position.setter @@ -110,6 +109,7 @@ def handle_position(self, new_pos: float) -> None: @QtCore.pyqtProperty(QtGui.QColor) def backgroundColor(self) -> QtGui.QColor: + """Widget property background color""" return self._backgroundColor @backgroundColor.setter @@ -119,6 +119,7 @@ def backgroundColor(self, new_color: QtGui.QColor) -> None: @QtCore.pyqtProperty(QtGui.QColor) def handleColor(self) -> QtGui.QColor: + """Widget property handle color""" return self._handleColor @handleColor.setter @@ -127,6 +128,7 @@ def handleColor(self, new_color: QtGui.QColor) -> None: self.update() def showEvent(self, a0: QtGui.QShowEvent) -> None: + """Re-implemented method, widget show""" _rect = self.contentsRect() self.trailPath: QtGui.QPainterPath = QtGui.QPainterPath() self.handlePath: QtGui.QPainterPath = QtGui.QPainterPath() @@ -155,16 +157,15 @@ def showEvent(self, a0: QtGui.QShowEvent) -> None: return super().showEvent(a0) def setPixmap(self, pixmap: QtGui.QPixmap) -> None: + """Set widget pixmap""" self.icon_pixmap = pixmap # self.repaint() self.update() @QtCore.pyqtSlot(name="clicked") def setup_animation(self) -> None: - if ( - not self.slide_animation.state - == self.slide_animation.State.Running - ): + """Setup widget animation""" + if not self.slide_animation.state == self.slide_animation.State.Running: self.slide_animation.setEndValue( self._handle_ONPosition if self.state == ToggleAnimatedButton.State.OFF @@ -173,23 +174,17 @@ def setup_animation(self) -> None: self.slide_animation.start() def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: + """Re-implemented method, handle mouse press events""" if self.trailPath: - if ( - self.trailPath.contains(e.pos().toPointF()) - and self.underMouse() - ): - if ( - not self.slide_animation.state - == self.slide_animation.State.Running - ): - self._state = ToggleAnimatedButton.State( - not self._state.value - ) + if self.trailPath.contains(e.pos().toPointF()) and self.underMouse(): + if not self.slide_animation.state == self.slide_animation.State.Running: + self._state = ToggleAnimatedButton.State(not self._state.value) self.stateChange.emit(self._state) super().mousePressEvent(e) e.ignore() def paintEvent(self, a0: QtGui.QPaintEvent) -> None: + """Re-implemented method, paint widget""" option = QtWidgets.QStyleOptionButton() option.initFrom(self) option.state |= QtWidgets.QStyle.StateFlag.State_Off @@ -197,7 +192,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: option.state |= QtWidgets.QStyle.StateFlag.State_Active _rect = self.contentsRect() - bg_color = (self.backgroundColor) + bg_color = self.backgroundColor self.handlePath: QtGui.QPainterPath = QtGui.QPainterPath() self.handle_ellipseRect = QtCore.QRectF( self._handle_position, @@ -211,8 +206,7 @@ def paintEvent(self, a0: QtGui.QPaintEvent) -> None: painter.setRenderHint(painter.RenderHint.SmoothPixmapTransform) painter.setBackgroundMode(QtCore.Qt.BGMode.TransparentMode) painter.setRenderHint(painter.RenderHint.LosslessImageRendering) - - + painter.fillPath( self.trailPath, bg_color if self.isEnabled() else self.disable_bg_color, diff --git a/BlocksScreen/lib/utils/ui.py b/BlocksScreen/lib/utils/ui.py deleted file mode 100644 index f74bd5df..00000000 --- a/BlocksScreen/lib/utils/ui.py +++ /dev/null @@ -1,2 +0,0 @@ -import typing - diff --git a/BlocksScreen/lib/utils/url.py b/BlocksScreen/lib/utils/url.py deleted file mode 100644 index 727cbb49..00000000 --- a/BlocksScreen/lib/utils/url.py +++ /dev/null @@ -1,76 +0,0 @@ - -class URLTYPE(object): - _prefix_type = ["ws://", "wss://", "http://", "https://"] - link_type = ["rest", "websocket"] - - def __init__(self, host: str, port=None, type: str = "rest"): - # self._prefix:str = - if isinstance(port, int) is False and port is not None: - raise AttributeError("If port is specified it can only be an integer") - - if type not in self.link_type: - raise AttributeError(f"Url type can only be of: {self.link_type}") - - self._websocket_suffix: str = "/websocket" - - self._host: str = host - self._port = port - self._type = type.lower() - self._build_url - # self._url = self._prefix_type[self._type] + self._host + ":" + str(self._port) + self._websocket_suffix - - def _build_url(self) -> None: - if self._type == "rest": - self._url = ( - self.link_type[2] + self._host - if self._host.endswith(".com") - else self.link_type[2] + self._host + ".com" - ) - - if self._type == "websocket": - self._url = ( - self.link_type[0] - + self._host - + ":" - + str(self._port) - + self._websocket_suffix - ) - - def type(self) -> str: - return self.__class__.__name__ - - @property - def url_link(self): - return self._url - - @url_link.setter - def url_link(self, host, port, type): - if self._type == "rest": - if port is None: - self._url = ( - self.link_type[2] + host - if host.endswith(".com") - else self.link_type[2] + host + ".com" - ) - else: - self._url = ( - self.link_type[2] + host + ":" + port - if host.endswith(".com") - else self.link_type[2] + host + ":" + port + ".com" - ) - - if self._type == "websocket": - self._url = ( - self.link_type[0] - + self._host - + ":" - + str(self._port) - + self._websocket_suffix - ) - - def __repr__(self) -> str: - cls = self.__class__.__name__ - return f"{cls}(host = {self._host}, port= {self._port}, type= {self._type})" - - def __str__(self) -> str: - return self._url \ No newline at end of file diff --git a/BlocksScreen/logger.py b/BlocksScreen/logger.py index 22de8402..e2aa90ca 100644 --- a/BlocksScreen/logger.py +++ b/BlocksScreen/logger.py @@ -21,6 +21,7 @@ def __init__( self.setLevel(level) def emit(self, record): + """Emit logging record""" try: msg = self.format(record) record = copy.copy(record) @@ -31,11 +32,8 @@ def emit(self, record): except Exception: self.handleError(record) - def flush(self): ... - - # TODO: Implement this - def setFormatter(self, fmt: logging.Formatter | None) -> None: + """Set logging formatter""" return super().setFormatter(fmt) @@ -44,12 +42,17 @@ class QueueListener(logging.handlers.TimedRotatingFileHandler): def __init__(self, filename, encoding="utf-8"): super(QueueListener, self).__init__( - filename=filename, when="MIDNIGHT", backupCount=10, encoding=encoding, delay=True + filename=filename, + when="MIDNIGHT", + backupCount=10, + encoding=encoding, + delay=True, ) self.queue = queue.Queue() - self._thread = threading.Thread(name=f"log.{filename}",target=self._run, daemon=True) + self._thread = threading.Thread( + name=f"log.{filename}", target=self._run, daemon=True + ) self._thread.start() - def _run(self): while True: @@ -62,26 +65,23 @@ def _run(self): break def close(self): + """Close logger listener""" if self._thread is None: return self.queue.put_nowait(None) self._thread.join() self._thread = None - def doRollover(self) -> None: ... - - # TODO: Implement this - def getFilesToDelete(self) -> list[str]: ... +global MainLoggingHandler - # TODO: Delete files that one month old -global MainLoggingHandler def create_logger( name: str = "log", level=logging.INFO, format: str = "'[%(levelname)s] | %(asctime)s | %(name)s | %(relativeCreated)6d | %(threadName)s : %(message)s", ): + """Create amd return logger""" global MainLoggingHandler logger = logging.getLogger(name) logger.setLevel(level) @@ -89,13 +89,3 @@ def create_logger( MainLoggingHandler = QueueHandler(ql.queue, format, level) logger.addHandler(MainLoggingHandler) return ql - - -def destroy_logger(name): ... # TODO: Implement this - - - - - - - diff --git a/BlocksScreen/screensaver.py b/BlocksScreen/screensaver.py index 8c733f0a..de02ba02 100644 --- a/BlocksScreen/screensaver.py +++ b/BlocksScreen/screensaver.py @@ -4,15 +4,9 @@ class ScreenSaver(QtCore.QObject): timer = QtCore.QTimer() - dpms_off_timeout = helper_methods.get_dpms_timeouts().get("off_timeout") - dpms_suspend_timeout = helper_methods.get_dpms_timeouts().get( - "suspend_timeout" - ) - dpms_standby_timeout = helper_methods.get_dpms_timeouts().get( - "standby_timeout" - ) - + dpms_suspend_timeout = helper_methods.get_dpms_timeouts().get("suspend_timeout") + dpms_standby_timeout = helper_methods.get_dpms_timeouts().get("standby_timeout") touch_blocked: bool = False def __init__(self, parent) -> None: @@ -23,9 +17,7 @@ def __init__(self, parent) -> None: ) if not self.screensaver_config: self.blank_timeout = ( - self.dpms_standby_timeout - if self.dpms_standby_timeout - else 900000 + self.dpms_standby_timeout if self.dpms_standby_timeout else 900000 ) else: self.blank_timeout = self.screensaver_config.getint( @@ -66,9 +58,6 @@ def eventFilter(self, object, event) -> bool: self.timer.start() return False - def timerEvent(self, a0: QtCore.QTimerEvent) -> None: - return super().timerEvent(a0) - def check_dpms(self) -> None: """Checks the X11 extension dpms for the status of the screen""" self.touch_blocked = True diff --git a/pyproject.toml b/pyproject.toml index f3be8e2f..9f7e1cfb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,35 +5,96 @@ description = "GUI for BLOCKS Printers running Klipper" authors = [ { name = "Hugo do Carmo Costa", email = "hugo.santos.costa@gmail.com" }, ] +maintainers = [ + { name = "Guilherme Costa", email = "guilherme.costa@blockstec.com" }, + { name = "Roberto Martins ", email = "roberto.martins@blockstec.com" }, +] dependencies = [ 'altgraph==0.17.4', - 'certifi==2024.7.4', - 'charset-normalizer==3.3.2', - 'idna==3.8', - 'numpy==2.1.0', - 'pefile==2023.2.7', - 'PyQt6==6.7.1', - 'PyQt6-Qt6==6.7.2', - 'PyQt6_sip==13.8.0', - 'requests==2.32.3', - 'sdbus==0.12.0', + 'certifi==2025.10.5', + 'charset-normalizer==3.4.4', + 'idna==3.11', + 'numpy==2.3.4', + 'pefile==2024.8.26', + 'PyQt6==6.10.0', + 'PyQt6-Qt6==6.10.0', + 'PyQt6_sip==13.10.2', + 'requests>=2.32.5', + 'sdbus==0.14.1', 'sdbus-networkmanager==2.0.0', 'typing==3.7.4.3', - 'websocket-client==1.8.0', - 'opencv-python-headless==4.11.0.86', - 'qrcode==8.2' + 'websocket-client==1.9.0', + 'qrcode==8.2', ] -requires-python = ">=3.11.2" +requires-python = "==3.11.2" readme = "README.md" license = { text = "GNU Affero General Public License v3.0 or later" } keywords = ["GUI", "klipper", "BlocksScreen", "BLOCKS"] +[project.optional-dependencies] +dev = ["ruff", "pylint", "pytest", "pytest-cov", "docstr_coverage"] +stage = ["bandit"] +full-dev = ["BlockScreen[dev,stage]"] + + +[project.urls] +Homepage = "https://blockstec.com" +Issues = "https://github.com/BlocksTechnology/BlocksScreen/issues" + [tool.ruff] line-length = 88 indent-width = 4 +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", + "BlocksScreen/lib/ui", + "extras", + "tests" +] + +[tool.ruff.lint] +ignore = ["F403"] [tool.ruff.format] indent-style = "space" line-ending = 'auto' docstring-code-format = true docstring-code-line-length = 94 + +[tool.pylint] +fail-under = 7 +jobs = 16 +ignore = ["tests", "scripts", "ui", "extras"] +ignore-paths = ["BlocksScreen/lib/ui"] +py-version = "3.11" +max-line-length = 88 + +[tool.pytest.ini_options] +addopts = "--cov=BlocksScreen --cov-report=html" + +[tool.bandit] +exclude_dirs = ["tests", "BlocksScreen/lib/ui/resources/"] diff --git a/scripts/requirements-dev.txt b/scripts/requirements-dev.txt index 5cbd2e7d..61706f13 100644 --- a/scripts/requirements-dev.txt +++ b/scripts/requirements-dev.txt @@ -2,3 +2,5 @@ ruff pylint pytest pytest-cov +docstr_coverage +bandit \ No newline at end of file diff --git a/scripts/requirements.txt b/scripts/requirements.txt index ca9730a2..9dfdbd57 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,16 +1,15 @@ altgraph==0.17.4 -certifi==2024.7.4 -charset-normalizer==3.3.2 -idna==3.8 -numpy==2.1.0 -pefile==2023.2.7 -PyQt6==6.7.1 -PyQt6-Qt6==6.7.2 -PyQt6_sip==13.8.0 -requests==2.32.4 -sdbus==0.12.0 +certifi==2025.10.5 +charset-normalizer==3.4.4 +idna==3.11 +numpy==2.3.4 +pefile==2024.8.26 +PyQt6==6.10.0 +PyQt6-Qt6==6.10.0 +PyQt6_sip==13.10.2 +requests>=2.32.5 +sdbus==0.14.1 sdbus-networkmanager==2.0.0 typing==3.7.4.3 -websocket-client==1.8.0 -opencv-python-headless==4.11.0.86 +websocket-client==1.9.0 qrcode==8.2 \ No newline at end of file