Skip to content

Commit

Permalink
file selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
Jhsmit committed Oct 30, 2024
1 parent 9e9e010 commit 5ea407b
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 93 deletions.
136 changes: 64 additions & 72 deletions dont_fret/web/bursts/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import dont_fret.web.state as state
from dont_fret.web.bursts.methods import create_histogram
from dont_fret.web.components import RangeInputField
from dont_fret.web.components import FigureFromTask, RangeInputField, RegexSelectDialog
from dont_fret.web.methods import chain_filters
from dont_fret.web.models import BinnedImage, BurstFilterItem, BurstNode, BurstPlotSettings
from dont_fret.web.new_models import FRETNode, FRETStore, ListStore
Expand Down Expand Up @@ -267,13 +267,15 @@ def on_field(value):


@solara.component
def FileFilterDialog(
burst_item: solara.Reactive[BurstNode],
def FileFilterDialogDepr(
value: list[str],
on_value: Callable[[list[str]], None],
values: list[str],
on_close: Callable[[], None],
):
"""update; selected files is stored elsewhere now"""
all_files = sorted(burst_item.value.df["filename"].unique())
local_selected_files = solara.use_reactive(cast(list[str], burst_item.value.selected_files))
local_selected_files = solara.use_reactive(value)
error, set_error = solara.use_state("")
regex, set_regex = solara.use_state("")

Expand Down Expand Up @@ -590,14 +592,6 @@ def set_selected_files(self, values: list[str]):
self.selection_store.value = new_value


# = VIEW
# REFACTOR
# def BurstFigure(
# seletion: is not a simple dataclass with reactives only
# node_tree = fret_store derived value which is updated when the node tree is updated (and thus reredner)
# )


def generate_figure(
df: pl.DataFrame,
plot_settings: BurstPlotSettings,
Expand Down Expand Up @@ -675,89 +669,87 @@ def generate_figure(

@solara.component
def BurstFigure(
selection: BurstFigureSelection,
selection: ListStore[str],
file_selection: dict[uuid.UUID, ListStore[str]],
):
figure, set_figure = solara.use_state(cast(Optional[go.Figure], None))
edit_settings = solara.use_reactive(False)
settings_dialog = solara.use_reactive(False)
file_filter_dialog = solara.use_reactive(False)
plot_settings = solara.use_reactive(
BurstPlotSettings()
) # -> these reset to default, combine with burstfigureselection?
) # -> these reset to default, move to global state?

dark_effective = solara.lab.use_dark_effective()

selected_file_names = [
node.name
for node in selection.burst_node.photon_nodes
if node.id.hex in selection.selected_files
]
file_filter = pl.col("filename").is_in(selected_file_names)
labels = ["Measurement", "Bursts"] # TODO move elsewhere
selector_nodes = make_selector_nodes(state.fret_nodes.items, "bursts")
# making the levels populates the selection
levels = list(NestedSelectors(nodes=selector_nodes, selection=selection, labels=labels))
burst_node = get_bursts(state.fret_nodes.items, selection.items)
filenames = sorted(burst_node.df["filename"].unique())
if burst_node.id in file_selection:
file_store = file_selection[burst_node.id]
else:
file_store = ListStore(filenames)
file_selection[burst_node.id] = file_store

# file_store = file_selection[
# burst_node.id
# ] # we make a new one here if it doestn exist yet, but should be OK

# # it should never be empty, only after making a new one, thus we set it to all selected if its emtp

# solara.Text(str(burst_node.id))

file_filter = pl.col("filename").is_in(file_store.items)
f_expr = chain_filters(state.filters.items) & file_filter

# this is triggered twice ? -> known plotly bug, use .key(...)
def redraw():
filtered_df = selection.burst_node.df.filter(f_expr)
print("redraw")
filtered_df = burst_node.df.filter(f_expr)
img = BinnedImage.from_settings(filtered_df, plot_settings.value)
figure = generate_figure(
filtered_df, plot_settings.value, binned_image=img, dark=dark_effective
)
set_figure(figure)
return figure

# todo upgrade to use_task
fig_result = solara.use_thread(
figure_task = solara.lab.use_task(
redraw,
dependencies=[
selection.burst_id.value,
selection.selected_files,
plot_settings.value,
state.filters.items,
dark_effective,
],
intrusive_cancel=False, # is much faster
dependencies=[burst_node.id, plot_settings.value, file_store.items, state.filters.items],
)

with solara.Card():
with solara.Row():
solara.Select(
label="Measurement", # TODO refactor to FRET node?
value=selection.fret_id.value.hex,
on_value=selection.set_fret_id, # type: ignore
values=selection.fret_values, # type: ignore
)
for level in levels:
solara.Select(**level)

solara.Select(
label="Burst item",
value=selection.burst_id.value.hex,
on_value=selection.set_burst_id,
values=selection.burst_values, # type: ignore
)
SelectCount(
label="Files",
value=selection.selected_files,
on_value=selection.set_selected_files,
items=selection.selected_files_values, # type: ignore
item_name="file",
solara.IconButton(
icon_name="mdi-file-star", on_click=lambda: file_filter_dialog.set(True)
)
# solara.IconButton(icon_name="mdi-file-star", on_click=lambda: set_edit_filter(True))
solara.IconButton(icon_name="mdi-settings", on_click=lambda: edit_settings.set(True))

solara.ProgressLinear(fig_result.state == solara.ResultState.RUNNING)
if figure is not None:
with solara.Div(
style="opacity: 0.3" if fig_result.state == solara.ResultState.RUNNING else None
):
solara.FigurePlotly(figure)
solara.IconButton(icon_name="mdi-settings", on_click=lambda: settings_dialog.set(True))

FigureFromTask(figure_task)

with solara.v.Dialog(
v_model=settings_dialog.value, max_width=750, on_v_model=settings_dialog.set
):
PlotSettingsEditDialog(
plot_settings,
burst_node.df.filter(f_expr), # = filtered dataframe by global filter
on_close=lambda: settings_dialog.set(False),
duration=burst_node.duration,
)

# dedent this and figure will flicker/be removed when opening the dialog
if edit_settings.value:
with rv.Dialog(
v_model=edit_settings.value, max_width=750, on_v_model=edit_settings.set
):
PlotSettingsEditDialog(
plot_settings,
selection.burst_node.df.filter(f_expr), # = filtered dataframe by global filter
on_close=lambda: edit_settings.set(False),
duration=selection.burst_node.duration,
)
with solara.v.Dialog(
v_model=file_filter_dialog.value, max_width=750, on_v_model=file_filter_dialog.set
):
RegexSelectDialog(
title="File Filter",
value=file_store.items,
on_value=file_store.set,
values=filenames,
on_close=lambda: file_filter_dialog.set(False),
)


@solara.component
Expand Down
14 changes: 8 additions & 6 deletions dont_fret/web/bursts/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ def BurstPage():
for filter_item in state.filters.items:
FilterListItem(filter_item)

if open_filter_dialog:
with solara.v.Dialog(
v_model=open_filter_dialog.value, max_width=1200, on_v_model=open_filter_dialog.set
):
with solara.Card(style={"width": "1000px"}):
FilterEditDialog()
# if open_filter_dialog:
with solara.v.Dialog(
v_model=open_filter_dialog.value, max_width=1200, on_v_model=open_filter_dialog.set
):
with solara.Card(style={"width": "1190px"}):
FilterEditDialog()

with solara.GridFixed(columns=2):
BurstFigure(
state.burst_figure_selection[0],
state.burst_figure_file_selection[0],
)
BurstFigure(
state.burst_figure_selection[1],
state.burst_figure_file_selection[1],
)
82 changes: 82 additions & 0 deletions dont_fret/web/components.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import re
from typing import Callable, Optional, Type

import solara
Expand Down Expand Up @@ -114,3 +115,84 @@ def handle(*args):

div = solara.Div(children=children)
solara.v.use_event(div, "dblclick", handle)


@solara.component
def FigureFromTask(task: solara.lab.Task):
solara.ProgressLinear(task.pending)
if task.latest is None:
solara.Text("loading...")
else:
figure = task.value if task.finished else task.latest
with solara.Div(style="opacity: 0.3" if task.pending else None):
solara.FigurePlotly(figure)


@solara.component
def RegexSelectDialog(
title: str,
value: list[str],
on_value: Callable[[list[str]], None],
values: list[str],
on_close: Callable[[], None],
sort: bool = True,
):
"""
select string by checkboxes or regex
"""
local_selection = solara.use_reactive(value)
error = solara.use_reactive("")
regex = solara.use_reactive("")

def on_input(value: str):
try:
pattern = re.compile(value)
regex.set(value)
error.set("")
except Exception:
error.set("Invalid regex")
return
new_selected = [f for f in values if pattern.search(f)]
local_selection.set(new_selected)

def on_save():
if not local_selection.value:
return
if sort:
on_value(sorted(local_selection.value))
else:
on_value(local_selection.value)
on_close()

with solara.Card(title):
with solara.Row(style="align-items: center;"):
solara.InputText(
label="regex",
value=regex.value,
on_value=on_input,
continuous_update=True,
error=error.value,
)
solara.Button(label="Select All", on_click=lambda: local_selection.set(values))
solara.Button(label="Select None", on_click=lambda: local_selection.set([]))
with solara.v.List(nav=True):
with solara.v.ListItemGroup(
v_model=local_selection.value,
on_v_model=local_selection.set,
multiple=True,
):
for v in values:
with solara.v.ListItem(value=v):
with solara.v.ListItemAction():
solara.Checkbox(value=v in local_selection.value)
solara.v.ListItemTitle(children=[v])

with solara.CardActions():
solara.v.Spacer()
solara.Button(
"Save",
icon_name="mdi-content-save",
on_click=on_save,
disabled=not local_selection.value,
)
solara.Button("Close", icon_name="mdi-window-close", on_click=on_close)
4 changes: 2 additions & 2 deletions dont_fret/web/new_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class _NoDefault:
class ListStore(Generic[T]):
"""baseclass for reactive list"""

def __init__(self, items: list[T]):
self._items = solara.reactive(items)
def __init__(self, items: Optional[list[T]] = None):
self._items = solara.reactive(items if items is not None else [])

def __len__(self):
return len(self.items)
Expand Down
7 changes: 5 additions & 2 deletions dont_fret/web/state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import copy
import dataclasses
import uuid
from collections import defaultdict
from pathlib import Path
from typing import Callable, Generic, Type, TypeVar

Expand Down Expand Up @@ -37,10 +38,12 @@
fret_nodes = FRETStore(TEST_NODES)

burst_figure_selection = [
BurstFigureSelection(fret_nodes),
BurstFigureSelection(fret_nodes),
ListStore[str]([]),
ListStore[str]([]),
]

burst_figure_file_selection = [{}, {}]

trace_selection = PhotonNodeSelection(fret_nodes)

# cfg set to dask manager
Expand Down
13 changes: 2 additions & 11 deletions dont_fret/web/trace/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import dont_fret.web.state as state
from dont_fret import BinnedPhotonData, PhotonData
from dont_fret.formatting import TRACE_COLORS, TRACE_SIGNS
from dont_fret.web.components import FigureFromTask
from dont_fret.web.methods import generate_traces
from dont_fret.web.models import (
BurstNode,
Expand Down Expand Up @@ -85,6 +86,7 @@ class PhotonNodeSelection:
)

def __post_init__(self):
print("deprecate")
# i want to remove this, we can check if the current id is in selection when accessing the property
self.fret_store._items.subscribe(self.on_fret_store)
self.reset()
Expand Down Expand Up @@ -165,17 +167,6 @@ def TracePage():
TCSPCFigure(photon_node)


@solara.component
def FigureFromTask(task: solara.lab.Task):
solara.ProgressLinear(task.pending)
if task.latest is None:
solara.Text("loading...")
else:
figure = task.value if task.finished else task.latest
with solara.Div(style="opacity: 0.3" if task.pending else None):
solara.FigurePlotly(figure)


@solara.component
def TraceFigure(photon_node: PhotonNode, settings: TraceSettings):
dark_effective = solara.lab.use_dark_effective()
Expand Down

0 comments on commit 5ea407b

Please sign in to comment.