Edit on GitHub

starplast.umap_gallery_panel

The "maps" dock: pregenerated maps to pick from, and how well each label maps onto each.

The gallery ships dozens of maps per organism (umap_gallery.recipes), so the list is a TREE grouped by what the map was built from -- all measurements, all but one kind of evidence, one evidence family, one individual experiment, and the pairs and triples of families -- with a thumbnail per map colored by its own clusters. It can be searched, sorted (by the label-free structure score, by how well the chosen label maps, or by how many genes a map places) and flattened into one best-first list. Each row states the map's recipe and its structure score, so the list can be read without opening anything.

Clicking a map puts it in the central 3D view through the window's own use_embedding, so it is a map like any other -- genes click, every color mode applies -- and its HDBSCAN clusters become the window's cluster coloring. Beside it, a sortable score table read in either direction:

  • One label, every map -- pick a label, see which map it separates on best;
  • One map, every label -- pick a map, see which labels it organises.

The map on screen is marked in both: with SHOWN_MARK on its row in the label view, and by being the only map in the map view. Picking a map therefore always changes the table, which is the bug this panel was rewritten around -- see refresh.

"Color by label" is one click. "Score the map on screen" runs the same scoring on whatever map the window shows (a map built in Analysis, say), and adds it to both tables as "on screen".

The panel holds no window reference: it emits what the user asked for, and install wires those requests to a window. That keeps it testable without a full application.

THUMB = 64
THUMB_POINTS = 2500
ON_SCREEN = 'on screen'
SHOWN_MARK = '▶ '
SORT_GROUP = 'Group, then gallery order'
SORT_STRUCTURE = 'Structure score, best first'
SORT_LABEL = 'How well the chosen label maps'
SORT_GENES = 'Genes placed, most first'
SORTS = ('Group, then gallery order', 'Structure score, best first', 'How well the chosen label maps', 'Genes placed, most first')
TABLE_COLUMNS = ('category_f1', 'category_skill', 'category_precision', 'category_recall', 'best_category', 'best_n', 'best_f1_lower', 'best_skill', 'coverage', 'circular')
def cluster_colors(clusters, theme: str = 'dark') -> numpy.ndarray:

RGBA per point: a categorical color per cluster, the theme's unknown gray for noise.

def recipe_line(record: dict) -> str:

One line naming what a map was built from and how it was settled: the row's second line.

def structure_line(record: dict) -> str:

The label-free structure score of a map and the two things it is made of.

class MapGalleryPanel(PyQt6.QtWidgets.QWidget):

Pregenerated maps for one organism, and the label x map score table.

MapGalleryPanel( organism: str, gallery: starplast.umap_gallery.Gallery | None = None, theme: str = 'dark', background=(0.06, 0.07, 0.09), parent=None)

Build the panel over one organism's part of gallery (the shipped one by default).

def map_chosen(unknown):

pyqtSignal(*types, name: str = ..., revision: int = ..., arguments: Sequence = ...) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal's arguments.

def color_label(unknown):

pyqtSignal(*types, name: str = ..., revision: int = ..., arguments: Sequence = ...) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal's arguments.

def color_clusters(unknown):

pyqtSignal(*types, name: str = ..., revision: int = ..., arguments: Sequence = ...) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal's arguments.

def score_screen(unknown):

pyqtSignal(*types, name: str = ..., revision: int = ..., arguments: Sequence = ...) -> PYQT_SIGNAL

types is normally a sequence of individual types. Each type is either a type object or a string that is the name of a C++ type. Alternatively each type could itself be a sequence of types each describing a different overloaded signal. name is the optional C++ name of the signal. If it is not specified then the name of the class attribute that is bound to the signal is used. revision is the optional revision of the signal that is exported to QML. If it is not specified then 0 is used. arguments is the optional sequence of the names of the signal's arguments.

organism
gallery
theme
background
scores
records
live
current_map
search
sort_box
group_check
tree
shown
view_box
label_box
map_box
table
color_btn
clusters_btn
score_btn
note
def labels(self) -> list:

Labels scored for this organism, in table order.

def all_scores(self) -> pandas.DataFrame:

Shipped scores plus the live score of the map on screen, if any.

def record(self, map_id: str) -> dict:

One map's manifest record, or an empty dict for the map on screen.

def title(self, map_id: str) -> str:

A map's readable name.

def label_skill(self) -> dict:

map id -> the chosen label's category_skill on it, for sorting and for the rows.

def items(self) -> list:

The map rows in display order: (map id, tree item). Headings are not rows.

def select_map(self, map_id: str):

Mark a map as the current one (list, map box, table) without asking the window.

def refresh(self, *_):

Refill the table for the chosen view, label and map.

Both views depend on the map that is current, which is what the rewrite fixed. The label view lists every map for one label, so its ROWS do not change when another map is picked -- but the marked row does, the selected row does, and the sentence under the table does, and those are what told the user nothing had happened. The map view is filtered to the current map, so picking a second map replaces its contents outright.

def set_live(self, scores: pandas.DataFrame):

File the scores of the map on screen under 'on screen' and show them.

def sentence(r, title: str) -> str:

One row of the table, in words.

def organism_of(window) -> str | None:

The window's space code.

def show_map( window, panel: MapGalleryPanel, map_id: str):

Put a gallery map in the central view, with its clusters as the window's clusters.

def color_by_label(window, label: str):

Color the main map by a label: the color-by box and the category color mode, together.

def score_on_screen( window, panel: MapGalleryPanel) -> pandas.DataFrame:

Score every label on the map the window shows; clusters it first when it has none.

def install(window) -> MapGalleryPanel | None:

Add the maps dock to a window, tabbed beside Analysis. None when no gallery ships for it.