Source code for matrix_viewer._tab_numpy


import numpy as np
import tkinter as tk
import tkinter.font
import time
from ._tab_table import ViewerTabTable

[docs]class ViewerTabNumpy(ViewerTabTable): """A viewer tab that can be used to visualize numpy.ndarray matrices and vectors.""" def __init__(self, viewer, matrix, matrix_title=None, font_size=None, cell_formatter=None): """Creates a new tab in the specified viewer. Please use viewer.view instead because this selects the appropriate Tab subclass.""" self.matrix = matrix self.num_dims = matrix.ndim if type(self.matrix).__name__ == "Tensor": self.matrix = self.matrix.cpu().numpy() # convert pytorch to numpy. This is like a copy else: # If the user modifies the value after running viewer.view, but before viewer.show, the new values # are displayed but one would expect that the state when viewer.view was called is displayed. Therefore, # make a copy self.matrix = self.matrix.copy() assert self.matrix.dtype.isbuiltin == 1, "matrix must be a type built-in into numpy (e. g. float32 ndarray), but it is a composed type or something else" if matrix_title is None: if self.num_dims == 1: matrix_title = f"{self.matrix.shape[0]} {self.matrix.dtype}" else: matrix_title = f"{self.matrix.shape[0]} x {self.matrix.shape[1]} {self.matrix.dtype}" self._calc_font(font_size) if self.matrix.dtype.kind == 'c': self.max_val = np.max(self.matrix.real) + np.max(self.matrix.imag) * 1j else: self.max_val = np.max(self.matrix) small_formatted_threshold = 0.1 max_value_for_fixed_point = 1e8 if cell_formatter is None: if self.matrix.dtype.kind in ['i', 'u']: # signed, unsigned integer self.float_formatter = "{:d}".format elif self.matrix.dtype.kind == 'b': # boolean self.float_formatter = "{}".format elif self.matrix.dtype.kind == 'f': if self.max_val >= max_value_for_fixed_point: self.float_formatter = "{:.6e}".format else: if np.sum(np.logical_and(self.matrix != 0, np.abs(self.matrix) < 1e-4)) < small_formatted_threshold * np.product(self.matrix.shape): # below 10% of the values is not looking nice with non-exponential format self.float_formatter = "{:.6f}".format else: self.float_formatter = "{:.6e}".format # use exponential format elif self.matrix.dtype.kind == 'c': # complex float (there is no complex int) if (self.max_val.real >= max_value_for_fixed_point) or (self.max_val.imag >= max_value_for_fixed_point): self.float_formatter = "{:.6e}".format else: if ((np.sum(np.logical_and(self.matrix.real != 0, np.abs(self.matrix.real) < 1e-4)) < small_formatted_threshold * np.product(self.matrix.shape)) and (np.sum(np.logical_and(self.matrix.imag != 0, np.abs(self.matrix.imag) < 1e-4)) < small_formatted_threshold * np.product(self.matrix.shape))): # below 10% of both the imag and the real part is not looking nice with non-exponential format self.float_formatter = "{:.6f}".format else: self.float_formatter = "{:.6e}".format # use exponential format else: # use string formatter as fallback self.float_formatter = "{}".format else: self.float_formatter = cell_formatter self.column_heading_formatter = "{:d}".format self.row_heading_formatter = "{:d}".format self._font_changed() if self.num_dims == 1: ViewerTabTable.__init__(self, viewer, matrix_title, 1, self.matrix.shape[0], highlight_selected_columns=False) else: ViewerTabTable.__init__(self, viewer, matrix_title, self.matrix.shape[1], self.matrix.shape[0]) self.canvas1.bind("<ButtonPress-1>", self._on_mouse_press) self.canvas1.bind("<ButtonRelease-1>", self._on_mouse_release) self.canvas1.bind("<Motion>", self._on_mouse_motion) def _font_changed(self): self.max_text_width = self.cell_font.measure('0' + self.float_formatter(self.max_val)) # add trailing 0 as a placeholder for better readability self.row_heading_text_width = self.cell_font.measure("0" * (len(str(self.matrix.shape[0] - 1)))) def _on_mouse_press(self, event): if (self._selection is not None) and (event.state & 0x01 == 0x01): # shift pressed self.mouse_press_start = self.old_mouse_press_start # if we start selecting a rectangle by moving the holded mouse to the right, then release the mouse button, and then press shift on a point left to the rectangle, the start point is needed because we do correct the actual selection rectangle so that end > start if self.mouse_press_start is not None: self._adjust_selection(event) else: self.mouse_press_start = None hit_x, hit_y = self._calc_hit_cell(event.x, event.y) if hit_x is None: self._selection = None self._focused_cell = None elif (hit_x == -1) and (hit_y == -1): self._selection = [0, 0, self.xscroll_items, self.yscroll_items] elif hit_x == -1: self._selection = [0, hit_y, self.xscroll_items, hit_y + 1] self._focused_cell = [0, hit_y] self.mouse_press_start = [-1, hit_y] elif hit_y == -1: self._selection = [hit_x, 0, hit_x + 1, self.yscroll_items] self._focused_cell = [hit_x, 0] self.mouse_press_start = [hit_x, -1] else: self._selection = [hit_x, hit_y, hit_x + 1, hit_y + 1] self._focused_cell = [hit_x, hit_y] self.mouse_press_start = [hit_x, hit_y] self._draw() def _on_mouse_release(self, event): self.old_mouse_press_start = self.mouse_press_start self.mouse_press_start = None def _on_mouse_motion(self, event): if self.mouse_press_start is not None: current_time = time.time() if self.last_autoscroll_time < current_time - self.autoscroll_delay: if self.mouse_press_start[0] != -1: if event.x < self.row_heading_width: self.xscroll_item = max(self.xscroll_item - 1, 0) self._scroll_x() self.last_autoscroll_time = current_time elif event.x > self.row_heading_width + self.xscroll_page_size * self.cell_width: self.xscroll_item = min(self.xscroll_item + 1, self.xscroll_max) self._scroll_x() self.last_autoscroll_time = current_time if self.mouse_press_start[1] != -1: if event.y < self.cell_height: self.yscroll_item = max(self.yscroll_item - 1, 0) self._scroll_y() self.last_autoscroll_time = current_time elif event.y > self.cell_height + self.yscroll_page_size * self.cell_height: self.yscroll_item = min(self.yscroll_item + 1, self.yscroll_max) self._scroll_y() self.last_autoscroll_time = current_time self._adjust_selection(event) self._draw() def _draw_cells(self): x = -self.cell_hpadding + self.row_heading_width y = self.cell_vpadding + self.cell_height for i_row in range(self.yscroll_item, min(self.yscroll_item + self.yscroll_page_size + 1, self.yscroll_items)): self.canvas1.create_text(x, y, text=self.row_heading_formatter(i_row), font=self.cell_font, anchor='ne') y += self.cell_height x += self.cell_width if self.num_dims == 1: for i_column in range(self.xscroll_item, min(self.xscroll_item + self.xscroll_page_size + 1, self.xscroll_items)): y = self.cell_vpadding self.canvas1.create_text(x, y, text='Value', font=self.cell_font, anchor='ne') y += self.cell_height for i_row in range(self.yscroll_item, min(self.yscroll_item + self.yscroll_page_size + 1, self.yscroll_items)): self.canvas1.create_text(x, y, text=self.float_formatter(self.matrix[i_row]), font=self.cell_font, anchor='ne') y += self.cell_height x += self.cell_width else: for i_column in range(self.xscroll_item, min(self.xscroll_item + self.xscroll_page_size + 1, self.xscroll_items)): y = self.cell_vpadding self.canvas1.create_text(x - self.max_text_width // 2, y, text=self.column_heading_formatter(i_column), font=self.cell_font, anchor='n') y += self.cell_height for i_row in range(self.yscroll_item, min(self.yscroll_item + self.yscroll_page_size + 1, self.yscroll_items)): self.canvas1.create_text(x, y, text=self.float_formatter(self.matrix[i_row, i_column]), font=self.cell_font, anchor='ne') y += self.cell_height x += self.cell_width
[docs] def get_selection(self): """Get the current selected matrix area. :return: [start0, end0, start1, end1] so that matrix[start0:end0, start1:end1] represents the selected part. If nothing was selected, returns None. If no area was explicitly selected, this is an 1x1 area representing the focused cell. """ if self._selection is None: return None else: return [self._selection[1], self._selection[3], self._selection[0], self._selection[2]]
[docs] def get_focused_cell(self): """Get the currently focused cell. This is the most recent cell that the user clicked on. If an area was selected that it drawn in blue, the focus cell is the cell with a white background inside the blue rectangle. :return: [index0, index1] so that matrix[index0, index1] represents the focused cell. """ if self._focused_cell is None: return None else: return [self._focused_cell[1], self._focused_cell[0]]
def matches_tab_numpy(object): return ((isinstance(object, np.ndarray) and (object.ndim <= 2) and (object.dtype.isbuiltin == 1)) or ((type(object).__name__ == "Tensor") and (object.ndim <= 2))) # pytorch