class_docstr: The PlateViewer class lets the user view the files associated with plates
  
  The idea here is that the PlateViewer is given a list of image files
  with plate, well and site metadata. The plate viewer organizes the
  files and lets the user browse individual plates.
class_docstr_tok: ["The","PlateViewer","class","lets","the","user","view","the","files","associated","with","plates","The","idea","here","is","that","the","PlateViewer","is","given","a","list","of","image","files","with","plate",",","well","and","site","metadata",".","The","plate","viewer","organizes","the","files","and","lets","the","user","browse","individual","plates","."]
code: def update_figure(self):
      if self.image_dict is None:
          return
      with self.image_dict_lock:
          image_dict = dict(
              [(x, y.copy()) for x, y in list(self.image_dict.items())]
          )
      channel_dict = {}
      totals = numpy.zeros(4)
      for i in range(self.channel_grid.GetNumberRows()):
          channel_name = self.channel_grid.GetRowLabelValue(i)
          channel_dict[channel_name] = numpy.array(
              [
                  int(self.channel_grid.GetCellValue(i, j))
                  for j in range(4)
              ],
              float,
          )
          totals += channel_dict[channel_name]
  
      site_dict = {}
      tile_dims = [0, 0]
      if self.use_site_grid:
          for i in range(self.site_grid.GetNumberRows()):
              site_name = self.site_grid.GetRowLabelValue(i)
              site_dict[site_name] = numpy.array(
                  # "or 1" because it might return an empty string for no value
                  [
                      float(self.site_grid.GetCellValue(i, j) or 1) - 1
                      for j in range(2)
                  ]
              )[::-1]
              tile_dims = [
                  max(i0, i1)
                  for i0, i1 in zip(site_dict[site_name], tile_dims)
              ]
      else:
          site_dict[None] = numpy.zeros(2)
      img_size = [0, 0]
      for sd in list(image_dict.values()):
          for channel in sd:
              img_size = [
                  max(i0, i1)
                  for i0, i1 in zip(sd[channel].shape, img_size)
              ]
      if all([iii == 0 for iii in img_size]):
          return
      img_size = numpy.array(img_size)
      tile_dims = numpy.array(tile_dims) + 1
      for k in site_dict:
          site_dict[k] *= img_size
      img_size = numpy.hstack(
          [numpy.ceil(tile_dims * img_size).astype(int), [3]]
      )
      megapicture = numpy.zeros(img_size, numpy.uint8)
      for site, sd in list(image_dict.items()):
          offs = site_dict[site].astype(int)
          # TO_DO - handle images that aren't scaled from 0 to 255
          for channel, image in list(sd.items()):
              imgmax = numpy.max(image)
              scale = (
                  1
                  if imgmax <= 1
                  else 255
                  if imgmax < 256
                  else 4095
                  if imgmax < 4096
                  else 65535
              )
              a = channel_dict[channel][3]
              rgb = channel_dict[channel][:3] / 255.0
              image = image * a / scale
              if image.ndim < 3:
                  image = (
                      image[:, :, numpy.newaxis]
                      * rgb[numpy.newaxis, numpy.newaxis, :]
                  )
  
              if image.shape[0] + offs[0] > megapicture.shape[0]:
                  image = image[
                      : (megapicture.shape[0] - offs[0]), :, :
                  ]
              if image.shape[1] + offs[1] > megapicture.shape[1]:
                  image = image[
                      :, : (megapicture.shape[1] - offs[1]), :
                  ]
              megapicture[
                  offs[0] : (offs[0] + image.shape[0]),
                  offs[1] : (offs[1] + image.shape[1]),
                  :,
              ] += image.astype(megapicture.dtype)
      self.axes.cla()
      self.axes.imshow(megapicture)
      self.canvas.draw()
      self.navtoolbar.update()
  
imports: import multiprocessing
  import threading
  import traceback
  import matplotlib
  import matplotlib.backends.backend_wx
  import matplotlib.backends.backend_wxagg
  import matplotlib.cm
  import matplotlib.figure
  import numpy
  import wx
  import wx.grid
  import scyjava
label_desc: Use image node_id 21 for calling the PlateViewer obj's underlying member method code with example usage: obj.update_figure() without return types