2025-03-07 08:03:18 +01:00

111 lines
3.9 KiB
Python

# SPDX-FileCopyrightText: © 2022-2023 Wojciech Trybus <wojtryb@gmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
from dataclasses import dataclass
from typing import List, Protocol
from PyQt5.QtCore import QByteArray
from ..enums import NodeType
from .node import Node, KritaNode
class KritaDocument(Protocol):
"""Krita `Document` object API."""
def activeNode(self) -> KritaNode: ...
def setActiveNode(self, node: KritaNode): ...
def createNode(self, name: str, node_type: NodeType) -> KritaNode: ...
def topLevelNodes(self) -> List[KritaNode]: ...
def resolution(self) -> int: ...
def currentTime(self) -> int: ...
def setCurrentTime(self, time: int) -> None: ...
def refreshProjection(self) -> None: ...
def annotation(self, type: str) -> QByteArray: ...
def annotationTypes(self) -> List[str]: ...
def setAnnotation(
self,
type: str,
description: str,
annotation: bytes) -> None: ...
@dataclass
class Document:
"""Wraps krita `Document` for typing, docs and PEP8 compatibility."""
document: KritaDocument
@property
def active_node(self) -> Node:
"""Settable property with this `Document`'s active `Node`."""
return Node(self.document.activeNode())
@active_node.setter
def active_node(self, node: Node) -> None:
"""Set active `Node`."""
self.document.setActiveNode(node.node)
def create_node(self, name: str, node_type: NodeType) -> Node:
"""
Create a Node.
IMPORTANT: Created node must be then added to node tree to be
usable from Krita. For example with add_child_node() method of
Node Class.
When relevant, the new Node will have the colorspace of the
image by default; that can be changed with Node::setColorSpace.
The settings and selections for relevant layer and mask types
can also be set after the Node has been created.
"""
return Node(self.document.createNode(name, node_type.value))
@property
def current_time(self) -> int:
"""Settable property with this `Document`'s current frame number."""
return self.document.currentTime()
@current_time.setter
def current_time(self, time: int) -> None:
"""Set current time using frame number"""
self.document.setCurrentTime(time)
def get_top_nodes(self) -> List[Node]:
"""Return a list of `Nodes` without a parent."""
return [Node(node) for node in self.document.topLevelNodes()]
def get_all_nodes(self, include_collapsed: bool = False) -> List[Node]:
"""Return a list of all `Nodes` in this document bottom to top."""
def recursive_search(nodes: List[Node], found_so_far: List[Node]):
for node in nodes:
if include_collapsed or not node.collapsed:
recursive_search(node.get_child_nodes(), found_so_far)
found_so_far.append(node)
return found_so_far
return recursive_search(self.get_top_nodes(), [])
@property
def dpi(self):
"""Return dpi (dot per inch) of the document."""
return self.document.resolution()
def refresh(self) -> None:
"""Refresh OpenGL projection of this document."""
self.document.refreshProjection()
def read_annotation(self, name: str) -> str:
"""Read annotation from .kra document parsed as string."""
return self.document.annotation(name).data().decode(encoding="utf-8")
def write_annotation(self, name: str, description: str, value: str):
"""Write annotation to .kra document."""
self.document.setAnnotation(
name,
description,
value.encode(encoding="utf-8"))
def contains_annotation(self, name: str) -> bool:
"""Return if annotation of given name is stored in .kra."""
return name in self.document.annotationTypes()