from typing import Optional
from paquo.colors import ColorType
from paquo.colors import QuPathColor
from paquo.java import PathClass
from paquo.java import PathClassFactory
from paquo.java import String
from paquo.java import compatibility
__all__ = ['QuPathPathClass']
[docs]class QuPathPathClass:
java_object: PathClass
[docs] @classmethod
def from_java(cls, path_class: PathClass) -> 'QuPathPathClass':
"""initialize a QuPathPathClass from its corresponding java PathClass"""
if not isinstance(path_class, PathClass):
raise TypeError("use PathClass(name='myclass') to instantiate")
# keep type annotation requirements intact by providing
# empty string, which is ignored when providing _java_path_class
return cls('', _java_path_class=path_class)
def __init__(self,
name: str,
color: Optional[ColorType] = None,
parent: Optional['QuPathPathClass'] = None,
**_kwargs) -> None:
"""create a QuPathPathClass
The QuPathPathClasses are wrappers around singletons defined by their
name and their ancestors. Internally there's only one java PathClass
instance per ancestor chain + name. (The internal unique id is chaining
names via ':', which is why ':' is an unsupported name character)
Parameters
----------
name:
the name of your path class
color:
a color (r,g,b) or (r,g,b,a) with 0 < x < 255 or a QuPathColor
if color is None, a color calculated from the name is used
parent:
the parent of the class
Returns
-------
path_class:
the QuPathPathClass
"""
# internal: check if a java path class was already provided
_java_path_class = _kwargs.pop('_java_path_class', None)
if _java_path_class is not None:
self.java_object = _java_path_class
return
# called by user
if name is None:
if parent is None:
# note:
# parent=None for name=None creates the root node on the qupath java side
# will have to consider if we expose this here.
raise NotImplementedError("creating Root PathClass is currently not supported")
else:
raise ValueError("cannot create derived QuPathPathClass with name=None")
elif isinstance(name, str):
if ":" in name or "\n" in name:
raise ValueError("PathClass names cannot contain ':' or '\n'")
name = String(name)
else:
raise TypeError(f"name requires type 'str' got '{type(name)}'")
# get the parent class if requested
java_parent = None
if parent is not None:
if not isinstance(parent, QuPathPathClass):
raise TypeError("parent must be a QuPathPathClass")
java_parent = parent.java_object
# set the color if requested
java_color = None
if color is not None:
java_color = QuPathColor.from_any(color).to_java_rgba() # use rgba?
if compatibility.supports_newer_addobject_and_pathclass():
path_class = PathClass.getInstance(java_parent, name, java_color)
else:
path_class = PathClassFactory.getDerivedPathClass(java_parent, name, java_color)
self.java_object = path_class
@property
def name(self) -> str:
"""the name of the class"""
return str(self.java_object.getName())
@property
def id(self) -> str:
"""the unique identifier string of the class"""
return str(self.java_object.toString())
def __eq__(self, other) -> bool:
# check if path classes are identical
if not isinstance(other, QuPathPathClass):
return False
return int(self.java_object.compareTo(other.java_object)) == 0
@property
def parent(self) -> Optional['QuPathPathClass']:
"""the parent path class of this path class"""
path_class = self.java_object.getParentClass()
if path_class is None:
return None
return QuPathPathClass.from_java(path_class)
@property
def origin(self) -> 'QuPathPathClass':
"""the toplevel parent of this path class"""
origin_class = self
while origin_class.parent is not None:
origin_class = origin_class.parent
return origin_class
[docs] def is_derived_from(self, parent_class: 'QuPathPathClass'):
"""is this class derived from the parent_class"""
return self.java_object.isDerivedFrom(parent_class.java_object)
[docs] def is_ancestor_of(self, child_class: 'QuPathPathClass'):
"""is this class an ancestor of the child_class"""
return self.java_object.isAncestorOf(child_class.java_object)
@property
def color(self) -> Optional[QuPathColor]:
"""return the path color"""
rgb = self.java_object.getColor()
if rgb is None:
return None
return QuPathColor.from_java_rgb(rgb)
@color.setter
def color(self, rgb: Optional[ColorType]) -> None:
"""set the path color"""
if rgb is not None:
rgb = QuPathColor.from_any(rgb).to_java_rgb() # maybe use argb?
self.java_object.setColor(rgb)
@property
def is_valid(self) -> bool:
"""check if the path class is valid"""
return bool(self.java_object.isValid())
@property
def is_derived_class(self) -> bool:
"""check if the class is a derived path class"""
return bool(self.java_object.isDerivedClass())
def __repr__(self) -> str:
return f"QuPathPathClass('{self.id}')"