Module QFlow.builders

Sub-modules

QFlow.builders.file
QFlow.builders.folder
QFlow.builders.icon

This module defines the Icon class, which extends QPixmap to handle image loading and scaling …

QFlow.builders.jsonf
QFlow.builders.object
QFlow.builders.tree

Classes

class File (filepath)
Expand source code
class File:
    """
    A generic file handler for reading, writing, and deleting files.
    
    This class provides a simple interface for common file operations with
    configurable read/write modes and encoding.
    
    Attributes:
        filepath (str): The path to the file to be managed.
        readType (str): The mode for reading files. Defaults to 'r'.
        encoding (str): The encoding used for file operations. Defaults to 'utf-8'.
        writeType (str): The mode for writing files. Defaults to 'w'.
    """
    
    def __init__(self, filepath) -> None:
        """
        Initializes the file handler with a specific filepath.
        
        Args:
            filepath (str): The path to the file to be managed.
        """
        self.filepath: str = filepath
        self.readType: str = 'r'
        self.encoding: str = 'utf-8'
        self.writeType: str = 'w'

    def readFile(self, lines: bool = False) -> str | list | None:
        """
        Reads the content of the file.
        
        Args:
            lines (bool, optional): If True, returns content as a list of lines.
                If False, returns content as a single string. Defaults to False.
        
        Returns:
            str | list | None: The file content as a string or list of lines,
                depending on the lines parameter.
        
        Raises:
            Exception: If the file is not found or an unexpected error occurs.
        """
        try:
            with open(file=self.filepath, mode=self.readType, encoding=self.encoding) as file:
                return file.read() if not lines else file.readlines()
        except FileNotFoundError:
            raise Exception(f"Error: File '{self.filepath}' does not exist.")
        except Exception as e:
            raise Exception(f"Unexpected error reading file: '{e}'.")

    def writeFile(self, data: str) -> bool:
        """
        Writes data to the file.
        
        Args:
            data (str): The content to write to the file.
        
        Returns:
            bool: True if the write operation was successful.
        
        Raises:
            Exception: If there are permission issues or an unexpected error occurs.
        """
        try:
            with open(file=self.filepath, mode=self.writeType, encoding=self.encoding) as file:
                file.write(data)

            return True
        except PermissionError:
            raise Exception(f"Error: You do not have permissions to write to '{self.filepath}'.")
        except Exception as e:
            raise Exception(f"Unexpected error writing to file: '{e}'.")

    def deleteFile(self) -> bool:
        """
        Deletes the file if it exists.
        
        Returns:
            bool: True if the file was successfully deleted, False if the file
                does not exist.
        """
        if os.path.exists(self.filepath):
            os.remove(self.filepath)
            return True
        
        return False

A generic file handler for reading, writing, and deleting files.

This class provides a simple interface for common file operations with configurable read/write modes and encoding.

Attributes
-----=
filepath : str
The path to the file to be managed.
readType : str
The mode for reading files. Defaults to 'r'.
encoding : str
The encoding used for file operations. Defaults to 'utf-8'.
writeType : str
The mode for writing files. Defaults to 'w'.

Initializes the file handler with a specific filepath.

Args
-----=
filepath : str
The path to the file to be managed.

Methods

def deleteFile(self) ‑> bool
Expand source code
def deleteFile(self) -> bool:
    """
    Deletes the file if it exists.
    
    Returns:
        bool: True if the file was successfully deleted, False if the file
            does not exist.
    """
    if os.path.exists(self.filepath):
        os.remove(self.filepath)
        return True
    
    return False

Deletes the file if it exists.

Returns
-----=
bool
True if the file was successfully deleted, False if the file does not exist.
def readFile(self, lines: bool = False) ‑> str | list | None
Expand source code
def readFile(self, lines: bool = False) -> str | list | None:
    """
    Reads the content of the file.
    
    Args:
        lines (bool, optional): If True, returns content as a list of lines.
            If False, returns content as a single string. Defaults to False.
    
    Returns:
        str | list | None: The file content as a string or list of lines,
            depending on the lines parameter.
    
    Raises:
        Exception: If the file is not found or an unexpected error occurs.
    """
    try:
        with open(file=self.filepath, mode=self.readType, encoding=self.encoding) as file:
            return file.read() if not lines else file.readlines()
    except FileNotFoundError:
        raise Exception(f"Error: File '{self.filepath}' does not exist.")
    except Exception as e:
        raise Exception(f"Unexpected error reading file: '{e}'.")

Reads the content of the file.

Args
-----=
lines : bool, optional
If True, returns content as a list of lines. If False, returns content as a single string. Defaults to False.
Returns
-----=
str | list | None
The file content as a string or list of lines, depending on the lines parameter.
Raises
-----=
Exception
If the file is not found or an unexpected error occurs.
def writeFile(self, data: str) ‑> bool
Expand source code
def writeFile(self, data: str) -> bool:
    """
    Writes data to the file.
    
    Args:
        data (str): The content to write to the file.
    
    Returns:
        bool: True if the write operation was successful.
    
    Raises:
        Exception: If there are permission issues or an unexpected error occurs.
    """
    try:
        with open(file=self.filepath, mode=self.writeType, encoding=self.encoding) as file:
            file.write(data)

        return True
    except PermissionError:
        raise Exception(f"Error: You do not have permissions to write to '{self.filepath}'.")
    except Exception as e:
        raise Exception(f"Unexpected error writing to file: '{e}'.")

Writes data to the file.

Args
-----=
data : str
The content to write to the file.
Returns
-----=
bool
True if the write operation was successful.
Raises
-----=
Exception
If there are permission issues or an unexpected error occurs.
class Folder (folderpath: str)
Expand source code
class Folder:
    """
    Class to manage folders easily.
    Allows creating, deleting, copying, moving, and listing folder contents.
    """
    
    def __init__(self, folderpath: str):
        """
        Initialize the folder manager.
        
        Args:
            folderpath: Path to the folder
        """
        self.folderpath = folderpath
        self.path = Path(folderpath)
    
    def create(self, existOk: bool = True) -> None:
        """
        Create the folder.
        
        Args:
            existOk: If True, don"t raise error if folder already exists
        
        Raises:
            FileExistsError: If folder exists and existOk is False
        """
        try:
            os.makedirs(self.folderpath, existOk=existOk)
        except FileExistsError:
            raise FileExistsError(f"Folder '{self.folderpath}' already exists")
    
    def delete(self, confirm: bool = False) -> bool:
        """
        Delete the folder and all its contents.
        
        Args:
            confirm: Safety flag to confirm deletion
        
        Returns:
            True if deleted, False if folder doesn"t exist
        
        Raises:
            ValueError: If confirm is False (safety measure)
        """
        if not confirm:
            raise ValueError("Must set confirm=True to delete folder")
        
        if self.exists():
            shutil.rmtree(self.folderpath)
            return True
        return False
    
    def exists(self) -> bool:
        """
        Check if the folder exists.
        
        Returns:
            True if exists, False otherwise
        """
        return os.path.exists(self.folderpath) and os.path.isdir(self.folderpath)
    
    def isEmpty(self) -> bool:
        """
        Check if the folder is empty.
        
        Returns:
            True if empty, False otherwise
        
        Raises:
            FileNotFoundError: If folder doesn"t exist
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        return len(os.listdir(self.folderpath)) == 0
    
    def listContents(self, pattern: Optional[str] = None, recursive: bool = False) -> List[str]:
        """
        List contents of the folder.
        
        Args:
            pattern: Optional pattern to filter files (e.g., "*.txt")
            recursive: If True, list contents recursively
        
        Returns:
            List of file/folder names
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        if recursive:
            contents = []
            for root, dirs, files in os.walk(self.folderpath):
                for item in dirs + files:
                    fullPath = os.path.join(root, item)
                    relPath = os.path.relpath(fullPath, self.folderpath)
                    if pattern is None or fnmatch.fnmatch(item, pattern):
                        contents.append(relPath)
            return contents
        else:
            contents = os.listdir(self.folderpath)
            if pattern:
                contents = [item for item in contents if fnmatch.fnmatch(item, pattern)]
            return contents
    
    def listFiles(self, pattern: Optional[str] = None, recursive: bool = False) -> List[str]:
        """
        List only files in the folder.
        
        Args:
            pattern: Optional pattern to filter files (e.g., "*.txt")
            recursive: If True, list files recursively
        
        Returns:
            List of absolute file paths
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        files = []
        if recursive:
            for root, _, filenames in os.walk(self.folderpath):
                for filename in filenames:
                    if pattern is None or fnmatch.fnmatch(filename, pattern):
                        fullPath = os.path.join(root, filename)
                        files.append(os.path.abspath(fullPath))
        else:
            for item in os.listdir(self.folderpath):
                itemPath = os.path.join(self.folderpath, item)
                if os.path.isfile(itemPath):
                    if pattern is None or fnmatch.fnmatch(item, pattern):
                        files.append(os.path.abspath(itemPath))
        
        return files
    
    def listFolders(self, recursive: bool = False) -> List[str]:
        """
        List only subfolders in the folder.
        
        Args:
            recursive: If True, list folders recursively
        
        Returns:
            List of folder names
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        folders = []
        if recursive:
            for root, dirnames, _ in os.walk(self.folderpath):
                for dirname in dirnames:
                    fullPath = os.path.join(root, dirname)
                    relPath = os.path.relpath(fullPath, self.folderpath)
                    folders.append(relPath)
        else:
            for item in os.listdir(self.folderpath):
                itemPath = os.path.join(self.folderpath, item)
                if os.path.isdir(itemPath):
                    folders.append(item)
        return folders
    
    def copyTo(self, destination: str) -> None:
        """
        Copy the folder to a new location.
        
        Args:
            destination: Destination path
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        shutil.copytree(self.folderpath, destination)
    
    def moveTo(self, destination: str) -> None:
        """
        Move the folder to a new location.
        
        Args:
            destination: Destination path
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        shutil.move(self.folderpath, destination)
        self.folderpath = destination
        self.path = Path(destination)
    
    def rename(self, newName: str) -> None:
        """
        Rename the folder.
        
        Args:
            newName: New folder name
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        parent = os.path.dirname(self.folderpath)
        newPath = os.path.join(parent, newName)
        os.rename(self.folderpath, newPath)
        self.folderpath = newPath
        self.path = Path(newPath)
    
    def getSize(self) -> int:
        """
        Get total size of the folder in bytes.
        
        Returns:
            Size in bytes
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        totalSize = 0
        for dirpath, dirnames, filenames in os.walk(self.folderpath):
            for filename in filenames:
                filepath = os.path.join(dirpath, filename)
                if os.path.exists(filepath):
                    totalSize += os.path.getsize(filepath)
        return totalSize
    
    def getSizeFormatted(self) -> str:
        """
        Get total size of the folder in human-readable format.
        
        Returns:
            Size as formatted string (e.g., "1.5 MB")
        """
        size = self.getSize()
        for unit in ["B", "KB", "MB", "GB", "TB"]:
            if size < 1024.0:
                return f"{size:.2f} {unit}"
            size /= 1024.0
        return f"{size:.2f} PB"
    
    def countItems(self, recursive: bool = False) -> dict:
        """
        Count files and folders.
        
        Args:
            recursive: If True, count recursively
        
        Returns:
            Dictionary with "files" and "folders" counts
        """
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        fileCount = 0
        folderCount = 0
        
        if recursive:
            for root, dirs, files in os.walk(self.folderpath):
                fileCount += len(files)
                folderCount += len(dirs)
        else:
            for item in os.listdir(self.folderpath):
                itemPath = os.path.join(self.folderpath, item)
                if os.path.isfile(itemPath):
                    fileCount += 1
                elif os.path.isdir(itemPath):
                    folderCount += 1
        
        return {"files": fileCount, "folders": folderCount}
    
    def createSubfolder(self, subfolderName: str) -> "Folder":
        """
        Create a subfolder inside this folder.
        
        Args:
            subfolderName: Name of the subfolder
        
        Returns:
            Folder instance for the new subfolder
        """
        subfolderPath = os.path.join(self.folderpath, subfolderName)
        subfolder = Folder(subfolderPath)
        subfolder.create()
        return subfolder
    
    def clear(self, confirm: bool = False) -> None:
        """
        Delete all contents of the folder but keep the folder itself.
        
        Args:
            confirm: Safety flag to confirm clearing
        
        Raises:
            ValueError: If confirm is False (safety measure)
        """
        if not confirm:
            raise ValueError("Must set confirm=True to clear folder")
        
        if not self.exists():
            raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
        
        for item in os.listdir(self.folderpath):
            itemPath = os.path.join(self.folderpath, item)
            if os.path.isfile(itemPath):
                os.remove(itemPath)
            elif os.path.isdir(itemPath):
                shutil.rmtree(itemPath)

Class to manage folders easily. Allows creating, deleting, copying, moving, and listing folder contents.

Initialize the folder manager.

Args
-----=
folderpath
Path to the folder

Methods

def clear(self, confirm: bool = False) ‑> None
Expand source code
def clear(self, confirm: bool = False) -> None:
    """
    Delete all contents of the folder but keep the folder itself.
    
    Args:
        confirm: Safety flag to confirm clearing
    
    Raises:
        ValueError: If confirm is False (safety measure)
    """
    if not confirm:
        raise ValueError("Must set confirm=True to clear folder")
    
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    for item in os.listdir(self.folderpath):
        itemPath = os.path.join(self.folderpath, item)
        if os.path.isfile(itemPath):
            os.remove(itemPath)
        elif os.path.isdir(itemPath):
            shutil.rmtree(itemPath)

Delete all contents of the folder but keep the folder itself.

Args
-----=
confirm
Safety flag to confirm clearing
Raises
-----=
ValueError
If confirm is False (safety measure)
def copyTo(self, destination: str) ‑> None
Expand source code
def copyTo(self, destination: str) -> None:
    """
    Copy the folder to a new location.
    
    Args:
        destination: Destination path
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    shutil.copytree(self.folderpath, destination)

Copy the folder to a new location.

Args
-----=
destination
Destination path
def countItems(self, recursive: bool = False) ‑> dict
Expand source code
def countItems(self, recursive: bool = False) -> dict:
    """
    Count files and folders.
    
    Args:
        recursive: If True, count recursively
    
    Returns:
        Dictionary with "files" and "folders" counts
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    fileCount = 0
    folderCount = 0
    
    if recursive:
        for root, dirs, files in os.walk(self.folderpath):
            fileCount += len(files)
            folderCount += len(dirs)
    else:
        for item in os.listdir(self.folderpath):
            itemPath = os.path.join(self.folderpath, item)
            if os.path.isfile(itemPath):
                fileCount += 1
            elif os.path.isdir(itemPath):
                folderCount += 1
    
    return {"files": fileCount, "folders": folderCount}

Count files and folders.

Args
-----=
recursive
If True, count recursively

Returns -----= Dictionary with "files" and "folders" counts

def create(self, existOk: bool = True) ‑> None
Expand source code
def create(self, existOk: bool = True) -> None:
    """
    Create the folder.
    
    Args:
        existOk: If True, don"t raise error if folder already exists
    
    Raises:
        FileExistsError: If folder exists and existOk is False
    """
    try:
        os.makedirs(self.folderpath, existOk=existOk)
    except FileExistsError:
        raise FileExistsError(f"Folder '{self.folderpath}' already exists")

Create the folder.

Args
-----=
existOk
If True, don"t raise error if folder already exists
Raises
-----=
FileExistsError
If folder exists and existOk is False
def createSubfolder(self, subfolderName: str) ‑> Folder
Expand source code
def createSubfolder(self, subfolderName: str) -> "Folder":
    """
    Create a subfolder inside this folder.
    
    Args:
        subfolderName: Name of the subfolder
    
    Returns:
        Folder instance for the new subfolder
    """
    subfolderPath = os.path.join(self.folderpath, subfolderName)
    subfolder = Folder(subfolderPath)
    subfolder.create()
    return subfolder

Create a subfolder inside this folder.

Args
-----=
subfolderName
Name of the subfolder

Returns -----= Folder instance for the new subfolder

def delete(self, confirm: bool = False) ‑> bool
Expand source code
def delete(self, confirm: bool = False) -> bool:
    """
    Delete the folder and all its contents.
    
    Args:
        confirm: Safety flag to confirm deletion
    
    Returns:
        True if deleted, False if folder doesn"t exist
    
    Raises:
        ValueError: If confirm is False (safety measure)
    """
    if not confirm:
        raise ValueError("Must set confirm=True to delete folder")
    
    if self.exists():
        shutil.rmtree(self.folderpath)
        return True
    return False

Delete the folder and all its contents.

Args
-----=
confirm
Safety flag to confirm deletion

Returns -----= True if deleted, False if folder doesn"t exist

Raises
-----=
ValueError
If confirm is False (safety measure)
def exists(self) ‑> bool
Expand source code
def exists(self) -> bool:
    """
    Check if the folder exists.
    
    Returns:
        True if exists, False otherwise
    """
    return os.path.exists(self.folderpath) and os.path.isdir(self.folderpath)

Check if the folder exists.

Returns -----= True if exists, False otherwise

def getSize(self) ‑> int
Expand source code
def getSize(self) -> int:
    """
    Get total size of the folder in bytes.
    
    Returns:
        Size in bytes
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    totalSize = 0
    for dirpath, dirnames, filenames in os.walk(self.folderpath):
        for filename in filenames:
            filepath = os.path.join(dirpath, filename)
            if os.path.exists(filepath):
                totalSize += os.path.getsize(filepath)
    return totalSize

Get total size of the folder in bytes.

Returns -----= Size in bytes

def getSizeFormatted(self) ‑> str
Expand source code
def getSizeFormatted(self) -> str:
    """
    Get total size of the folder in human-readable format.
    
    Returns:
        Size as formatted string (e.g., "1.5 MB")
    """
    size = self.getSize()
    for unit in ["B", "KB", "MB", "GB", "TB"]:
        if size < 1024.0:
            return f"{size:.2f} {unit}"
        size /= 1024.0
    return f"{size:.2f} PB"

Get total size of the folder in human-readable format.

Returns -----= Size as formatted string (e.g., "1.5 MB")

def isEmpty(self) ‑> bool
Expand source code
def isEmpty(self) -> bool:
    """
    Check if the folder is empty.
    
    Returns:
        True if empty, False otherwise
    
    Raises:
        FileNotFoundError: If folder doesn"t exist
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    return len(os.listdir(self.folderpath)) == 0

Check if the folder is empty.

Returns -----= True if empty, False otherwise

Raises
-----=
FileNotFoundError
If folder doesn"t exist
def listContents(self, pattern: str | None = None, recursive: bool = False) ‑> List[str]
Expand source code
def listContents(self, pattern: Optional[str] = None, recursive: bool = False) -> List[str]:
    """
    List contents of the folder.
    
    Args:
        pattern: Optional pattern to filter files (e.g., "*.txt")
        recursive: If True, list contents recursively
    
    Returns:
        List of file/folder names
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    if recursive:
        contents = []
        for root, dirs, files in os.walk(self.folderpath):
            for item in dirs + files:
                fullPath = os.path.join(root, item)
                relPath = os.path.relpath(fullPath, self.folderpath)
                if pattern is None or fnmatch.fnmatch(item, pattern):
                    contents.append(relPath)
        return contents
    else:
        contents = os.listdir(self.folderpath)
        if pattern:
            contents = [item for item in contents if fnmatch.fnmatch(item, pattern)]
        return contents

List contents of the folder.

Args
-----=
pattern
Optional pattern to filter files (e.g., "*.txt")
recursive
If True, list contents recursively

Returns -----= List of file/folder names

def listFiles(self, pattern: str | None = None, recursive: bool = False) ‑> List[str]
Expand source code
def listFiles(self, pattern: Optional[str] = None, recursive: bool = False) -> List[str]:
    """
    List only files in the folder.
    
    Args:
        pattern: Optional pattern to filter files (e.g., "*.txt")
        recursive: If True, list files recursively
    
    Returns:
        List of absolute file paths
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    files = []
    if recursive:
        for root, _, filenames in os.walk(self.folderpath):
            for filename in filenames:
                if pattern is None or fnmatch.fnmatch(filename, pattern):
                    fullPath = os.path.join(root, filename)
                    files.append(os.path.abspath(fullPath))
    else:
        for item in os.listdir(self.folderpath):
            itemPath = os.path.join(self.folderpath, item)
            if os.path.isfile(itemPath):
                if pattern is None or fnmatch.fnmatch(item, pattern):
                    files.append(os.path.abspath(itemPath))
    
    return files

List only files in the folder.

Args
-----=
pattern
Optional pattern to filter files (e.g., "*.txt")
recursive
If True, list files recursively

Returns -----= List of absolute file paths

def listFolders(self, recursive: bool = False) ‑> List[str]
Expand source code
def listFolders(self, recursive: bool = False) -> List[str]:
    """
    List only subfolders in the folder.
    
    Args:
        recursive: If True, list folders recursively
    
    Returns:
        List of folder names
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    folders = []
    if recursive:
        for root, dirnames, _ in os.walk(self.folderpath):
            for dirname in dirnames:
                fullPath = os.path.join(root, dirname)
                relPath = os.path.relpath(fullPath, self.folderpath)
                folders.append(relPath)
    else:
        for item in os.listdir(self.folderpath):
            itemPath = os.path.join(self.folderpath, item)
            if os.path.isdir(itemPath):
                folders.append(item)
    return folders

List only subfolders in the folder.

Args
-----=
recursive
If True, list folders recursively

Returns -----= List of folder names

def moveTo(self, destination: str) ‑> None
Expand source code
def moveTo(self, destination: str) -> None:
    """
    Move the folder to a new location.
    
    Args:
        destination: Destination path
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    shutil.move(self.folderpath, destination)
    self.folderpath = destination
    self.path = Path(destination)

Move the folder to a new location.

Args
-----=
destination
Destination path
def rename(self, newName: str) ‑> None
Expand source code
def rename(self, newName: str) -> None:
    """
    Rename the folder.
    
    Args:
        newName: New folder name
    """
    if not self.exists():
        raise FileNotFoundError(f"Folder '{self.folderpath}' does not exist")
    
    parent = os.path.dirname(self.folderpath)
    newPath = os.path.join(parent, newName)
    os.rename(self.folderpath, newPath)
    self.folderpath = newPath
    self.path = Path(newPath)

Rename the folder.

Args
-----=
newName
New folder name
class Icon (path: str, w: int, h: int)
Expand source code
class Icon(QPixmap):
    """
    Planned for package use only.
    
    A QPixmap subclass that loads and scales an image if the file exists.

    This class checks whether the specified image file exists before loading it.
    If the file is found, it scales the image while maintaining its aspect ratio.
    Otherwise, it initializes an empty QPixmap.
    """

    def __init__(self, path: str, w: int, h: int):
        """
        Initializes an Icon object.

        Args:
            path (str): The file path of the image.
            w (int): The desired width of the icon.
            h (int): The desired height of the icon.
        """
        if os.path.exists(path):
            pixmap = QPixmap(path).scaled(w, h, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
            super().__init__(pixmap)
        else:
            super().__init__()

Planned for package use only.

A QPixmap subclass that loads and scales an image if the file exists.

This class checks whether the specified image file exists before loading it. If the file is found, it scales the image while maintaining its aspect ratio. Otherwise, it initializes an empty QPixmap.

Initializes an Icon object.

Args
-----=
path : str
The file path of the image.
w : int
The desired width of the icon.
h : int
The desired height of the icon.

Ancestors

  • PyQt6.QtGui.QPixmap
  • PyQt6.QtGui.QPaintDevice
  • PyQt6.sip.simplewrapper
class JSON (filepath: str)
Expand source code
class JSON:
    """
    Class to manage JSON files easily.
    Allows reading, writing, editing, and manipulating JSON files.
    """

    def __init__(self, filepath: str):
        self.filepath = filepath
        self.data = None

    @privatemethod
    def resolve(self, data: Any, keys: list, create: bool = False) -> tuple[Any, str]:
        """
        Walk data following keys and return (parent, lastKey).

        Args:
            data:   Root object to traverse.
            keys:   List of key segments already split by '.'.
            create: If True, missing intermediate dicts are created.

        Returns:
            (parentNode, finalKey) so the caller can do parent[key].

        Raises:
            KeyError:  A segment does not exist and create=False.
            TypeError: A segment exists but its value is not a dict.
        """
        node = data
        for key in keys[:-1]:
            if isinstance(node, dict):
                if key not in node:
                    if create:
                        node[key] = {}
                    else:
                        raise KeyError(f"Key '{key}' not found.")
                node = node[key]
            else:
                raise TypeError(f"Expected a dict at '{key}', got {type(node).__name__}.")
        return node, keys[-1]

    def read(self) -> Dict[str, Any]:
        try:
            with open(self.filepath, 'r', encoding='utf-8') as f:
                self.data = json.load(f)
            return self.data
        except FileNotFoundError:
            raise FileNotFoundError(f"The file '{self.filepath}' does not exist.")
        except json.JSONDecodeError as e:
            raise json.JSONDecodeError(f"Error decoding JSON: '{e}'", e.doc, e.pos)

    def write(self, data: Dict[str, Any], indent: int = 4) -> None:
        with open(self.filepath, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=indent, ensure_ascii=False)
        self.data = data

    def create(self, data: Dict[str, Any] = None, indent: int = 4) -> None:
        self.write(data or {}, indent)

    def reload(self) -> Dict[str, Any]:
        return self.read()

    def exists(self) -> bool:
        return os.path.exists(self.filepath)
    
    def getAll(self) -> Dict[str, Any]:
        """
        Get all data.
        """
        if self.data is None:
            self.read()

        return self.data

    def get(self, key: str, default: Any = None) -> Any:
        """
        Get a value using dot-notation key.
        """
        if self.data is None:
            self.read()
        try:
            parent, lastKey = self.resolve(self.data, key.split('.'))
            return parent.get(lastKey, default) if isinstance(parent, dict) else default
        except (KeyError, TypeError):
            return default

    def set(self, key: str, value: Any) -> None:
        """
        Set a value using dot-notation key, creating intermediate dicts when needed.
        """
        if self.data is None:
            self.read()
        parent, lastKey = self.resolve(self.data, key.split('.'), create=True)
        parent[lastKey] = value
        self.write(self.data)

    def delete(self, key: str) -> bool:
        """
        Delete a key using dot-notation. Returns True if deleted, False if not found.
        """
        if self.data is None:
            self.read()
        try:
            parent, lastKey = self.resolve(self.data, key.split('.'))
            if isinstance(parent, dict) and lastKey in parent:
                del parent[lastKey]
                self.write(self.data)
                return True
            return False
        except (KeyError, TypeError):
            return False

    def update(self, key: str, value: Any) -> None:
        """Update a top-level key (use set() for nested keys)."""
        if self.data is None:
            self.read()
        self.data[key] = value
        self.write(self.data)

    def merge(self, newData: Dict[str, Any]) -> None:
        if self.data is None:
            self.read()
        self.data.update(newData)
        self.write(self.data)

    def clear(self) -> None:
        self.data = {}
        self.write(self.data)

    def keys(self) -> List[str]:
        if self.data is None:
            self.read()
        return list(self.data.keys())

    def values(self) -> List[Any]:
        if self.data is None:
            self.read()
        return list(self.data.values())

    def items(self) -> List[tuple]:
        if self.data is None:
            self.read()
        return list(self.data.items())

    def indexar(self, data: Any = None, root: Dict[str, Any] = None) -> int:
        """
        Traverse all values in the JSON and replace '${key.key}/literal'
        references with their resolved value plus any surrounding literal text.

        Args:
            data: Current node (used internally for recursion).
            root: JSON root (used internally to resolve paths).

        Returns:
            Number of replacements made.

        Raises:
            ValueError: If a reference does not exist in the JSON or points
                        to a non-scalar value (dict or list).
        """
        PATTERN = re.compile(r'\$\{([^}]+)\}')

        if root is None:
            if self.data is None:
                self.read()
            root = self.data
            data = self.data

        def resolveRef(refKey: str) -> str:
            try:
                parent, lastKey = self.resolve(root, refKey.split('.'))
            except (KeyError, TypeError):
                raise ValueError(f"Reference '${{{refKey}}}' does not exist in the JSON.")
            if not isinstance(parent, dict) or lastKey not in parent:
                raise ValueError(f"Reference '${{{refKey}}}' does not exist in the JSON.")
            value = parent[lastKey]
            if isinstance(value, (dict, list)):
                raise ValueError(
                    f"Reference '${{{refKey}}}' points to an object/list, not a scalar value."
                )
            return str(value)

        def processNode(node: Any) -> tuple[Any, int]:
            count = 0

            if isinstance(node, dict):
                for key in node:
                    node[key], subCount = processNode(node[key])
                    count += subCount

            elif isinstance(node, list):
                for i, item in enumerate(node):
                    node[i], subCount = processNode(item)
                    count += subCount

            elif isinstance(node, str) and '${' in node:
                def replacer(match):
                    nonlocal count
                    count += 1
                    return resolveRef(match.group(1))
                node = PATTERN.sub(replacer, node)

            return node, count

        self.data, totalReplacements = processNode(data)
        return totalReplacements

Class to manage JSON files easily. Allows reading, writing, editing, and manipulating JSON files.

Methods

def clear(self) ‑> None
Expand source code
def clear(self) -> None:
    self.data = {}
    self.write(self.data)
def create(self, data: Dict[str, Any] = None, indent: int = 4) ‑> None
Expand source code
def create(self, data: Dict[str, Any] = None, indent: int = 4) -> None:
    self.write(data or {}, indent)
def delete(self, key: str) ‑> bool
Expand source code
def delete(self, key: str) -> bool:
    """
    Delete a key using dot-notation. Returns True if deleted, False if not found.
    """
    if self.data is None:
        self.read()
    try:
        parent, lastKey = self.resolve(self.data, key.split('.'))
        if isinstance(parent, dict) and lastKey in parent:
            del parent[lastKey]
            self.write(self.data)
            return True
        return False
    except (KeyError, TypeError):
        return False

Delete a key using dot-notation. Returns True if deleted, False if not found.

def exists(self) ‑> bool
Expand source code
def exists(self) -> bool:
    return os.path.exists(self.filepath)
def get(self, key: str, default: Any = None) ‑> Any
Expand source code
def get(self, key: str, default: Any = None) -> Any:
    """
    Get a value using dot-notation key.
    """
    if self.data is None:
        self.read()
    try:
        parent, lastKey = self.resolve(self.data, key.split('.'))
        return parent.get(lastKey, default) if isinstance(parent, dict) else default
    except (KeyError, TypeError):
        return default

Get a value using dot-notation key.

def getAll(self) ‑> Dict[str, Any]
Expand source code
def getAll(self) -> Dict[str, Any]:
    """
    Get all data.
    """
    if self.data is None:
        self.read()

    return self.data

Get all data.

def indexar(self, data: Any = None, root: Dict[str, Any] = None) ‑> int
Expand source code
def indexar(self, data: Any = None, root: Dict[str, Any] = None) -> int:
    """
    Traverse all values in the JSON and replace '${key.key}/literal'
    references with their resolved value plus any surrounding literal text.

    Args:
        data: Current node (used internally for recursion).
        root: JSON root (used internally to resolve paths).

    Returns:
        Number of replacements made.

    Raises:
        ValueError: If a reference does not exist in the JSON or points
                    to a non-scalar value (dict or list).
    """
    PATTERN = re.compile(r'\$\{([^}]+)\}')

    if root is None:
        if self.data is None:
            self.read()
        root = self.data
        data = self.data

    def resolveRef(refKey: str) -> str:
        try:
            parent, lastKey = self.resolve(root, refKey.split('.'))
        except (KeyError, TypeError):
            raise ValueError(f"Reference '${{{refKey}}}' does not exist in the JSON.")
        if not isinstance(parent, dict) or lastKey not in parent:
            raise ValueError(f"Reference '${{{refKey}}}' does not exist in the JSON.")
        value = parent[lastKey]
        if isinstance(value, (dict, list)):
            raise ValueError(
                f"Reference '${{{refKey}}}' points to an object/list, not a scalar value."
            )
        return str(value)

    def processNode(node: Any) -> tuple[Any, int]:
        count = 0

        if isinstance(node, dict):
            for key in node:
                node[key], subCount = processNode(node[key])
                count += subCount

        elif isinstance(node, list):
            for i, item in enumerate(node):
                node[i], subCount = processNode(item)
                count += subCount

        elif isinstance(node, str) and '${' in node:
            def replacer(match):
                nonlocal count
                count += 1
                return resolveRef(match.group(1))
            node = PATTERN.sub(replacer, node)

        return node, count

    self.data, totalReplacements = processNode(data)
    return totalReplacements

Traverse all values in the JSON and replace '${key.key}/literal' references with their resolved value plus any surrounding literal text.

Args
-----=
data
Current node (used internally for recursion).
root
JSON root (used internally to resolve paths).

Returns -----= Number of replacements made.

Raises
-----=
ValueError
If a reference does not exist in the JSON or points to a non-scalar value (dict or list).
def items(self) ‑> List[tuple]
Expand source code
def items(self) -> List[tuple]:
    if self.data is None:
        self.read()
    return list(self.data.items())
def keys(self) ‑> List[str]
Expand source code
def keys(self) -> List[str]:
    if self.data is None:
        self.read()
    return list(self.data.keys())
def merge(self, newData: Dict[str, Any]) ‑> None
Expand source code
def merge(self, newData: Dict[str, Any]) -> None:
    if self.data is None:
        self.read()
    self.data.update(newData)
    self.write(self.data)
def read(self) ‑> Dict[str, Any]
Expand source code
def read(self) -> Dict[str, Any]:
    try:
        with open(self.filepath, 'r', encoding='utf-8') as f:
            self.data = json.load(f)
        return self.data
    except FileNotFoundError:
        raise FileNotFoundError(f"The file '{self.filepath}' does not exist.")
    except json.JSONDecodeError as e:
        raise json.JSONDecodeError(f"Error decoding JSON: '{e}'", e.doc, e.pos)
def reload(self) ‑> Dict[str, Any]
Expand source code
def reload(self) -> Dict[str, Any]:
    return self.read()
def resolve(self, data: Any, keys: list, create: bool = False) ‑> tuple[typing.Any, str]
Expand source code
@privatemethod
def resolve(self, data: Any, keys: list, create: bool = False) -> tuple[Any, str]:
    """
    Walk data following keys and return (parent, lastKey).

    Args:
        data:   Root object to traverse.
        keys:   List of key segments already split by '.'.
        create: If True, missing intermediate dicts are created.

    Returns:
        (parentNode, finalKey) so the caller can do parent[key].

    Raises:
        KeyError:  A segment does not exist and create=False.
        TypeError: A segment exists but its value is not a dict.
    """
    node = data
    for key in keys[:-1]:
        if isinstance(node, dict):
            if key not in node:
                if create:
                    node[key] = {}
                else:
                    raise KeyError(f"Key '{key}' not found.")
            node = node[key]
        else:
            raise TypeError(f"Expected a dict at '{key}', got {type(node).__name__}.")
    return node, keys[-1]

Walk data following keys and return (parent, lastKey).

Args
-----=
data
Root object to traverse.
keys
List of key segments already split by '.'.
create
If True, missing intermediate dicts are created.

Returns -----= (parentNode, finalKey) so the caller can do parent[key].

Raises
-----=
KeyError
A segment does not exist and create=False.
TypeError
A segment exists but its value is not a dict.
def set(self, key: str, value: Any) ‑> None
Expand source code
def set(self, key: str, value: Any) -> None:
    """
    Set a value using dot-notation key, creating intermediate dicts when needed.
    """
    if self.data is None:
        self.read()
    parent, lastKey = self.resolve(self.data, key.split('.'), create=True)
    parent[lastKey] = value
    self.write(self.data)

Set a value using dot-notation key, creating intermediate dicts when needed.

def update(self, key: str, value: Any) ‑> None
Expand source code
def update(self, key: str, value: Any) -> None:
    """Update a top-level key (use set() for nested keys)."""
    if self.data is None:
        self.read()
    self.data[key] = value
    self.write(self.data)

Update a top-level key (use set() for nested keys).

def values(self) ‑> List[Any]
Expand source code
def values(self) -> List[Any]:
    if self.data is None:
        self.read()
    return list(self.data.values())
def write(self, data: Dict[str, Any], indent: int = 4) ‑> None
Expand source code
def write(self, data: Dict[str, Any], indent: int = 4) -> None:
    with open(self.filepath, 'w', encoding='utf-8') as f:
        json.dump(data, f, indent=indent, ensure_ascii=False)
    self.data = data
class Object (data: dict)
Expand source code
class Object:
    """Class to create an object using a dictionary."""
    def __init__(self, data: dict):
        self.obj = self.toNamespace(data)

    def toNamespace(self, data):
        if isinstance(data, dict):
            return SimpleNamespace(**{k: self.toNamespace(v) for k, v in data.items()})
        elif isinstance(data, list):
            return [self.toNamespace(item) for item in data]
        else:
            return data

Class to create an object using a dictionary.

Methods

def toNamespace(self, data)
Expand source code
def toNamespace(self, data):
    if isinstance(data, dict):
        return SimpleNamespace(**{k: self.toNamespace(v) for k, v in data.items()})
    elif isinstance(data, list):
        return [self.toNamespace(item) for item in data]
    else:
        return data
class Tree (configFile: str = 'config.json')
Expand source code
class Tree:
    """
    Tree that scans the folders/patterns defined in a JSON config file and
    exposes their absolute paths through:

        tree.f['folder', 'subfolder', ...]

    Prefix the key with 'unf' to resolve the SAME relative path against the
    normal (non-frozen) base path instead of the frozen one:

        tree.f['unf', 'folder', 'subfolder', ...]

    The config JSON must have the form:
        {
            "folders": ["app", "app/**/*.html"]
        }

    Patterns are resolved with glob (supports recursive '**') relative to
    the project's base path (see `basePath`), which changes automatically
    if the program is compiled/frozen.
    """

    def __init__(self, configFile: str = 'config.json'):
        self.root = Tree.basePath()
        self.normalRoot = Tree.normalPath()
        cfgPath = (
            configFile if os.path.isabs(configFile)
            else os.path.join(self.root, configFile)
        )
        self.config = self.loadConfig(cfgPath)
        self.f = self.buildTree(getattr(self.config, 'folders', []))

    def loadConfig(self, path: str) -> SimpleNamespace:
        if not os.path.isfile(path):
            raise FileNotFoundError(f"Config file not found: '{path}'.")
        
        with open(path, 'r', encoding='utf-8') as fh:
            data = json.load(fh)

        return Object(data).obj

    def buildTree(self, patterns) -> Node:
        root = Node(self.root, root=self.root, normalRoot=self.normalRoot)
        for pattern in patterns:
            patternPath = os.path.join(self.root, pattern)
            matches = glob.glob(patternPath, recursive=True)
            if not matches:
                matches = [patternPath]
            for absolutePath in matches:
                relative = os.path.relpath(absolutePath, self.root)
                parts = relative.split(os.sep)
                self.insertNode(root, parts, os.path.abspath(absolutePath))
        return root

    def insertNode(self, node: Node, parts: list, absolutePath: str):
        current = node
        accumulated = self.root
        for part in parts:
            accumulated = os.path.join(accumulated, part)
            if part not in current:
                dict.__setitem__(current, part, Node(os.path.abspath(accumulated)))
            current = dict.__getitem__(current, part)
        current.path = absolutePath

    @staticmethod
    def basePath() -> str:
        """
        Absolute base path of the project.

        - If the executable is frozen (PyInstaller or another bundler):
            * sys._MEIPASS -> --onefile mode (resources extracted to a temp folder)
            * dirname(sys.executable) -> --onedir mode (resources next to the binary)
        - If running as a normal script, uses the directory of the script that
        started the process (sys.argv[0]).
        """
        if FROZENLIB:
            return os.path.abspath(sys._MEIPASS)
        return os.path.dirname(os.path.abspath(sys.argv[0]))

    @staticmethod
    def normalPath() -> str:
        """
        Absolute path of the folder that actually contains the running
        script/executable, regardless of freezing mode.

        Unlike `basePath`, this NEVER returns a --onefile temp extraction
        folder (sys._MEIPASS): it always points to where the .exe/script
        physically lives, which is what you want for files that must be
        read/written alongside the binary (logs, user config, output, etc.)
        rather than looked up inside the bundled resources.
        """
        if FROZENLIB:
            return os.path.dirname(os.path.abspath(sys.executable))
        return os.path.dirname(os.path.abspath(sys.argv[0]))

Tree that scans the folders/patterns defined in a JSON config file and exposes their absolute paths through:

tree.f['folder', 'subfolder', ...]

Prefix the key with 'unf' to resolve the SAME relative path against the normal (non-frozen) base path instead of the frozen one:

tree.f['unf', 'folder', 'subfolder', ...]

The config JSON must have the form: { "folders": ["app", "app/*/.html"] }

Patterns are resolved with glob (supports recursive '**') relative to the project's base path (see basePath), which changes automatically if the program is compiled/frozen.

Static methods

def basePath() ‑> str
Expand source code
@staticmethod
def basePath() -> str:
    """
    Absolute base path of the project.

    - If the executable is frozen (PyInstaller or another bundler):
        * sys._MEIPASS -> --onefile mode (resources extracted to a temp folder)
        * dirname(sys.executable) -> --onedir mode (resources next to the binary)
    - If running as a normal script, uses the directory of the script that
    started the process (sys.argv[0]).
    """
    if FROZENLIB:
        return os.path.abspath(sys._MEIPASS)
    return os.path.dirname(os.path.abspath(sys.argv[0]))

Absolute base path of the project.

  • If the executable is frozen (PyInstaller or another bundler):
    • sys._MEIPASS -> –onefile mode (resources extracted to a temp folder)
    • dirname(sys.executable) -> –onedir mode (resources next to the binary)
  • If running as a normal script, uses the directory of the script that started the process (sys.argv[0]).
def normalPath() ‑> str
Expand source code
@staticmethod
def normalPath() -> str:
    """
    Absolute path of the folder that actually contains the running
    script/executable, regardless of freezing mode.

    Unlike `basePath`, this NEVER returns a --onefile temp extraction
    folder (sys._MEIPASS): it always points to where the .exe/script
    physically lives, which is what you want for files that must be
    read/written alongside the binary (logs, user config, output, etc.)
    rather than looked up inside the bundled resources.
    """
    if FROZENLIB:
        return os.path.dirname(os.path.abspath(sys.executable))
    return os.path.dirname(os.path.abspath(sys.argv[0]))

Absolute path of the folder that actually contains the running script/executable, regardless of freezing mode.

Unlike basePath, this NEVER returns a –onefile temp extraction folder (sys._MEIPASS): it always points to where the .exe/script physically lives, which is what you want for files that must be read/written alongside the binary (logs, user config, output, etc.) rather than looked up inside the bundled resources.

Methods

def buildTree(self, patterns) ‑> Node
Expand source code
def buildTree(self, patterns) -> Node:
    root = Node(self.root, root=self.root, normalRoot=self.normalRoot)
    for pattern in patterns:
        patternPath = os.path.join(self.root, pattern)
        matches = glob.glob(patternPath, recursive=True)
        if not matches:
            matches = [patternPath]
        for absolutePath in matches:
            relative = os.path.relpath(absolutePath, self.root)
            parts = relative.split(os.sep)
            self.insertNode(root, parts, os.path.abspath(absolutePath))
    return root
def insertNode(self,
node: Node,
parts: list,
absolutePath: str)
Expand source code
def insertNode(self, node: Node, parts: list, absolutePath: str):
    current = node
    accumulated = self.root
    for part in parts:
        accumulated = os.path.join(accumulated, part)
        if part not in current:
            dict.__setitem__(current, part, Node(os.path.abspath(accumulated)))
        current = dict.__getitem__(current, part)
    current.path = absolutePath
def loadConfig(self, path: str) ‑> types.SimpleNamespace
Expand source code
def loadConfig(self, path: str) -> SimpleNamespace:
    if not os.path.isfile(path):
        raise FileNotFoundError(f"Config file not found: '{path}'.")
    
    with open(path, 'r', encoding='utf-8') as fh:
        data = json.load(fh)

    return Object(data).obj