Skip to content

Commit

Permalink
implement plane viewing and clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
AnniekStok committed Aug 12, 2024
1 parent 45f7aea commit fe4fda3
Show file tree
Hide file tree
Showing 10 changed files with 1,195 additions and 1,102 deletions.
6 changes: 4 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ project_urls =
packages = find:
install_requires =
napari
numpy
scikit-image
napari-skimage-regionprops
dask_image
Expand All @@ -41,8 +40,11 @@ install_requires =
diplib
localthickness
skan
numpy==1.26.4
napari-plane-sliders

python_requires = >=3.8
dependency_links = https://github.com/AnniekStok/napari-plane-sliders/tarball/main#egg=napari-plane-sliders
python_requires = >=3.10
include_package_data = True
package_dir =
=src
Expand Down
6 changes: 3 additions & 3 deletions src/napari_lumen_segmentation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
__version__ = "0.0.1"
from ._widget import AnnotateLabelsND
from ._custom_table_widget import ColoredTableWidget, TableWidget
from ._distance_widget import DistanceWidget
from ._histogram_widget import HistWidget
from ._layer_dropdown import LayerDropdown
from ._skeleton_widget import SkeletonWidget
from ._distance_widget import DistanceWidget
from ._widget import AnnotateLabelsND

__all__ = (
"AnnotateLabelsND",
Expand All @@ -13,5 +13,5 @@
"TableWidget",
"HistWidget",
"LayerDropdown",
"DistanceWidget"
"DistanceWidget",
)
112 changes: 68 additions & 44 deletions src/napari_lumen_segmentation/_custom_table_widget.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,53 @@

import napari

import pandas as pd
from pandas import DataFrame

from matplotlib.colors import ListedColormap, to_rgb
from napari_skimage_regionprops import TableWidget
from matplotlib.colors import to_rgb, ListedColormap

from qtpy.QtWidgets import QTableWidget, QHBoxLayout, QTableWidgetItem, QWidget, QGridLayout, QPushButton, QFileDialog

from pandas import DataFrame
from qtpy.QtGui import QColor
from qtpy.QtWidgets import (
QFileDialog,
QGridLayout,
QHBoxLayout,
QPushButton,
QTableWidget,
QTableWidgetItem,
QWidget,
)


class ColoredTableWidget(TableWidget):
"""Customized table widget based on the napari_skimage_regionprops TableWidget
"""
"""Customized table widget based on the napari_skimage_regionprops TableWidget"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.ascending = False # for choosing whether to sort ascending or descending
self.ascending = (
False # for choosing whether to sort ascending or descending
)

# Reconnect the clicked signal to your custom method.
self._view.clicked.connect(self._clicked_table)

# Connect to single click in the header to sort the table.
self._view.horizontalHeader().sectionClicked.connect(self._sort_table)


def _set_label_colors_to_rows(self) -> None:
"""Apply the colors of the napari label image to the table"""

for i in range(self._view.rowCount()):
label = self._table['label'][i]
label = self._table["label"][i]
label_color = to_rgb(self._layer.get_color(label))
scaled_color = (int(label_color[0] * 255), int(label_color[1] * 255), int(label_color[2] * 255))
scaled_color = (
int(label_color[0] * 255),
int(label_color[1] * 255),
int(label_color[2] * 255),
)
for j in range(self._view.columnCount()):
self._view.item(i, j).setBackground(QColor(*scaled_color))

def _clicked_table(self):
"""Also set show_selected_label to True and jump to the corresponding stack position"""

super()._clicked_table()
self._layer.show_selected_label = True

Expand All @@ -49,28 +56,34 @@ def _clicked_table(self):
current_step = self._viewer.dims.current_step
if len(current_step) == 4:
new_step = (current_step[0], z, current_step[2], current_step[3])
elif len(current_step) == 3:
elif len(current_step) == 3:
new_step = (z, current_step[1], current_step[2])
else:
else:
new_step = current_step
self._viewer.dims.current_step = new_step

def _sort_table(self):
"""Sorts the table in ascending or descending order"""

selected_column = list(self._table.keys())[self._view.currentColumn()]
df = pd.DataFrame(self._table).sort_values(by=selected_column, ascending=self.ascending)
df = pd.DataFrame(self._table).sort_values(
by=selected_column, ascending=self.ascending
)
self.ascending = not self.ascending

self.set_content(df.to_dict(orient='list'))
self.set_content(df.to_dict(orient="list"))
self._set_label_colors_to_rows()


class TableWidget(QWidget):
"""
The table widget represents a table inside napari.
Tables are just views on `properties` of `layers`.
"""
def __init__(self, props=pd.DataFrame(), viewer: "napari.Viewer" = None ):

def __init__(
self, props: pd.DataFrame | None, viewer: "napari.Viewer" = None
):
super().__init__()

self._viewer = viewer
Expand All @@ -82,7 +95,10 @@ def __init__(self, props=pd.DataFrame(), viewer: "napari.Viewer" = None ):
self.ascending = False
self._view.horizontalHeader().sectionClicked.connect(self._sort_table)

self.props = props.to_dict(orient='list')
if props is None:
self.props = pd.DataFrame().to_dict(orient="list")
else:
self.props = props.to_dict(orient="list")
self.set_content(self.props)

copy_button = QPushButton("Copy to clipboard")
Expand All @@ -102,12 +118,16 @@ def __init__(self, props=pd.DataFrame(), viewer: "napari.Viewer" = None ):
action_widget.layout().setContentsMargins(0, 0, 0, 0)

def _save_clicked(self, event=None, filename=None):
if filename is None: filename, _ = QFileDialog.getSaveFileName(self, "Save as csv...", ".", "*.csv")
if filename is None:
filename, _ = QFileDialog.getSaveFileName(
self, "Save as csv...", ".", "*.csv"
)
DataFrame(self._table).to_csv(filename)

def _copy_clicked(self): DataFrame(self._table).to_clipboard()
def _copy_clicked(self):
DataFrame(self._table).to_clipboard()

def set_content(self, table : dict):
def set_content(self, table: dict):
"""
Overwrites the content of the table with the content of a given dictionary.
"""
Expand All @@ -134,35 +154,39 @@ def get_content(self) -> dict:
Returns the current content of the table
"""
return self._table

def _sort_table(self):
"""Sorts the table in ascending or descending order"""

selected_column = list(self._table.keys())[self._view.currentColumn()]
df = pd.DataFrame(self._table).sort_values(by=selected_column, ascending=self.ascending)
df = pd.DataFrame(self._table).sort_values(
by=selected_column, ascending=self.ascending
)
self.ascending = not self.ascending
self.set_content(df.to_dict(orient='list'))
self.set_content(df.to_dict(orient="list"))
if self.sort_by is not None:
self._recolor(self.sort_by, self.colormap)
def _recolor(self, by:str, cmap:ListedColormap):
"""Assign colors to the table based on given column and colormap """
self._recolor(self.sort_by, self.colormap)

def _recolor(self, by: str, cmap: ListedColormap):
"""Assign colors to the table based on given column and colormap"""

default_color = self.palette().color(self.backgroundRole())
if by is None:
for i in range(self._view.rowCount()):
if by is None:
for i in range(self._view.rowCount()):
for j in range(self._view.columnCount()):
self._view.item(i, j).setBackground(default_color)

else:
for i in range(self._view.rowCount()):
id = self._table[by][i]
color = to_rgb(cmap.colors[id])
scaled_color = (int(color[0] * 255), int(color[1] * 255), int(color[2] * 255))
else:
for i in range(self._view.rowCount()):
label = self._table[by][i]
color = to_rgb(cmap.colors[label])
scaled_color = (
int(color[0] * 255),
int(color[1] * 255),
int(color[2] * 255),
)
for j in range(self._view.columnCount()):
self._view.item(i, j).setBackground(QColor(*scaled_color))

self.sort_by = by
self.colormap = cmap


Loading

0 comments on commit fe4fda3

Please sign in to comment.