def __setitem__(self, k: str, v: str) -> None: # noinspection PyProtectedMember if self._image._readonly: raise AttributeError("project in readonly mode") if not isinstance(k, str): raise TypeError(f"key must be of type `str` got `{type(k)}`") if not isinstance(v, str): raise TypeError(f"value must be of type `str` got `{type(v)}`") self._entry.putMetadataValue(String(str(k)), String(str(v)))
def __getitem__(self, k: str) -> Any: if not isinstance(k, str): raise TypeError(f"key must be of type `str` got `{type(k)}`") if k not in self: raise KeyError(f"'{k}' not in metadata") v = self._image_data.getProperty(String(k)) return v
def __delitem__(self, k: str) -> None: # noinspection PyProtectedMember if self._image._readonly: raise AttributeError("project in readonly mode") if not isinstance(k, str): raise TypeError(f"key must be of type `str` got `{type(k)}`") self._image_data.removeProperty(String(k))
def __getitem__(self, k: str) -> str: if not isinstance(k, str): raise TypeError(f"key must be of type `str` got `{type(k)}`") v = self._entry.getMetadataValue(String(str(k))) if v is None: raise KeyError(f"'{k}' not in metadata") return str(v)
def add_image( self, image_id: Any, # this should actually be ID type of the image provider image_type: Optional[QuPathImageType] = None, *, allow_duplicates: bool = False) -> QuPathProjectImageEntry: """add an image to the project Parameters ---------- image_id: image_id pointing to the image file (with default image_provider: filename) image_type: provide an image type for the image. If not provided the user will be prompted before opening the image in QuPath. allow_duplicates: check if file has already been added to the project. """ # readonly? if self._readonly: raise OSError("project in readonly mode") # test if we may add: img_uri = self._image_provider.uri(image_id) if img_uri is None: raise FileNotFoundError( f"image_provider can't provide URI for requested image_id: '{image_id}'" ) img_id = self._image_provider.id(img_uri) if not img_id == image_id: # pragma: no cover _log.warning( f"image_provider roundtrip error: '{image_id}' -> uri -> '{img_id}'" ) raise RuntimeError( "the image provider failed to roundtrip the image id correctly" ) if not allow_duplicates: for entry in self.images: uri = self._image_provider.id(entry.uri) if img_id == uri: raise FileExistsError(img_id) # first get a server builder try: support = ImageServerProvider.getPreferredUriImageSupport( BufferedImage, String(str(img_uri))) except IOException: # pragma: no cover # it's possible that an image_provider returns an URI but that URI # is not actually reachable. In that case catch the java IOException # and raise a FileNotFoundError here raise FileNotFoundError(image_id) except ExceptionInInitializerError: raise OSError("no preferred support found") if not support: raise OSError("no preferred support found") # pragma: no cover server_builders = list(support.getBuilders()) if not server_builders: raise OSError( "no supported server builders found") # pragma: no cover server_builder = server_builders[0] with self._stage_image_entry(server_builder) as j_entry: # all of this happens in qupath.lib.gui.commands.ProjectImportImagesCommand try: server = server_builder.build() except IOException: _, _, _sb = server_builder.__class__.__name__.rpartition(".") raise OSError(f"{_sb} can't open {str(image_id)}") j_entry.setImageName(ServerTools.getDisplayableImageName(server)) # add some informative logging _md = server.getMetadata() width = int(_md.getWidth()) height = int(_md.getHeight()) downsamples = [ float(x) for x in _md.getPreferredDownsamplesArray() ] target_downsample = math.sqrt(width / 1024.0 * height / 1024.0) _log.info( f"Image[{width}x{height}] with downsamples {downsamples}") if not any(d >= target_downsample for d in downsamples): _log.warning( "No matching downsample for thumbnail! This might take a long time..." ) # set the project thumbnail try: thumbnail = ProjectImportImagesCommand_getThumbnailRGB( server, None) except NegativeArraySizeException: # pragma: no cover raise RuntimeError( "Thumbnailing FAILED. Image might be too large and has no embedded thumbnail." ) else: j_entry.setThumbnail(thumbnail) py_entry = self._image_entries_proxy[-1] if image_type is not None: py_entry.image_type = image_type # save project after adding image py_entry.save() self.save(images=False) return py_entry
def description(self, value: str): if not isinstance(value, str): raise TypeError("requires a str") self.java_object.setDescription(String(value))
def name(self: PathROIObjectType, name: Union[str, None]) -> None: if name is not None: name = String(name) self.java_object.setName(name) if self._update_callback: self._update_callback(self)
def from_geojson(cls: Type[PathROIObjectType], geojson) -> PathROIObjectType: """create a new Path Object from geojson""" gson = GsonTools.getInstance() java_obj = gson.fromJson(String(json.dumps(geojson)), cls.java_class) return cls(java_obj)
def image_name(self, name: str) -> None: if self._readonly: raise AttributeError("project in readonly mode") self.java_object.setImageName(String(name))
def __contains__(self, item: Any) -> bool: if not isinstance(item, str): return False return bool(self._image_data.getProperties().containsKey(String(item)))
def __contains__(self, item): return bool(self._entry.containsMetadata(String(str(item))))
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? path_class = PathClassFactory.getDerivedPathClass( java_parent, name, java_color) self.java_object = path_class