Skip to content

Commit

Permalink
add xml widget (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlambert03 authored Jun 26, 2021
1 parent 4766b66 commit 6d4fca7
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ repos:
rev: "v0.902"
hooks:
- id: mypy
exclude: ^testing|^docs
exclude: ^testing|^docs|_napari_plugin
5 changes: 5 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ classifiers =
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9

[options]
zip_safe = False
Expand All @@ -32,6 +33,10 @@ install_requires =
xmlschema==1.4.1
Pint>=0.15

[options.entry_points]
napari.plugin =
ome-types = ome_types._napari_plugin

[options.package_data]
* = *.xsd
ome_types = py.typed
Expand Down
111 changes: 111 additions & 0 deletions src/ome_types/_napari_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import warnings
from typing import Union

from napari_plugin_engine import napari_hook_implementation
from qtpy.QtCore import QMimeData, Qt
from qtpy.QtWidgets import QTreeWidget, QTreeWidgetItem

from ome_types import OME


@napari_hook_implementation
def napari_experimental_provide_dock_widget():
return OMETree, {"name": "OME Metadata Viewer"}


@napari_hook_implementation
def napari_get_reader(path):
"""Show OME XML if an ome.xml file is dropped on the viewer."""
if isinstance(path, str) and path.endswith("ome.xml"):
return view_ome_xml


def view_ome_xml(path):
from napari._qt.qt_main_window import _QtMainWindow

# close your eyes, or look away...
# there is nothing worth looking at here!
window = _QtMainWindow.current()
if not window:
return
viewer = window.qt_viewer.viewer
dw, widget = viewer.window.add_plugin_dock_widget("ome-types")
widget.update(path)

return [(None,)] # sentinel


class OMETree(QTreeWidget):
"""A Widget that can show OME XML"""

def __init__(self, ome_dict: dict = None, parent=None) -> None:
super().__init__(parent=parent)
self.setAcceptDrops(True)
self.setDropIndicatorShown(True)
self.setHeaderHidden(True)
self.update(ome_dict)

def update(self, ome: Union[OME, str]):
if not ome:
return
if isinstance(ome, str):
try:
if ome.endswith(".xml"):
ome = OME.from_xml(ome)
elif ome.lower().endswith((".tif", ".tiff")):
ome = OME.from_tiff(ome)
else:
warnings.warn(f"Unrecognized file type: {ome}")
return
except Exception as e:
warnings.warn(f"Could not parse OME metadata from {ome}: {e}")
return

self._fill_item(ome.dict(exclude_unset=True))

def _fill_item(self, obj, item: QTreeWidgetItem = None):
if item is None:
self.clear()
item = self.invisibleRootItem()
if isinstance(obj, dict):
for key, val in sorted(obj.items()):
child = QTreeWidgetItem([key])
item.addChild(child)
self._fill_item(val, child)
elif isinstance(obj, (list, tuple)):
for n, val in enumerate(obj):
text = val.get("id", n) if hasattr(val, "get") else n
child = QTreeWidgetItem([str(text)])
item.addChild(child)
self._fill_item(val, child)
else:
t = getattr(obj, "value", str(obj))
item.setText(0, f"{item.text(0)}: {t}")

def dropMimeData(
self, parent: QTreeWidgetItem, index: int, data: QMimeData, a
) -> bool:
if data.hasUrls():
for url in data.urls():
lf = url.toLocalFile()
if lf.endswith((".xml", ".tiff", ".tif")):
self.update(lf)
return True
return False

def mimeTypes(self):
return ["text/uri-list"]

def supportedDropActions(self):
return Qt.CopyAction


if __name__ == "__main__":
from qtpy.QtWidgets import QApplication

app = QApplication([])

widget = OMETree()
widget.show()

app.exec()
14 changes: 14 additions & 0 deletions testing/test_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pathlib import Path

import pytest

nplg = pytest.importorskip("ome_types._napari_plugin")

DATA = Path(__file__).parent / "data"


@pytest.mark.parametrize("fname", DATA.iterdir(), ids=lambda x: x.stem)
def test_widget(fname, qtbot):
if fname.stem in ("bad.ome", "timestampannotation.ome"):
pytest.xfail()
nplg.OMETree(str(fname))

0 comments on commit 6d4fca7

Please sign in to comment.