          self._image_canvas = ImagesCanvas(container, self._tk_vars)
          container.add(self._image_canvas, weight=3)
  
          options_frame = ttk.Frame(container)
          self._cli_frame = ActionFrame(
              options_frame,
              self._available_masks,
              self._samples.predictor.has_predicted_mask,
              self._patch.converter.cli_arguments.color_adjustment.replace("-", "_"),
              self._patch.converter.cli_arguments.mask_type.replace("-", "_"),
              self._config_tools,
              self._refresh,
              self._samples.generate,
              self._tk_vars)
          self._opts_book = OptionsBook(options_frame,
                                        self._config_tools,
                                        self._refresh)
          container.add(options_frame, weight=1)
          self.update_idletasks()
 
</s> remove         self._image_canvas = ImagesCanvas(container, self._tk_vars)
 </s> add         self._image_canvas = ImagesCanvas(self, container) </s> remove 
        self._tk_vars: Dict[Literal["refresh", "busy"],
                            tk.BooleanVar] = dict(refresh=tk.BooleanVar(), busy=tk.BooleanVar())
        for val in self._tk_vars.values():
            val.set(False)
        self._display = FacesDisplay(256, 64, self._tk_vars)

        trigger_patch = Event()
        self._samples = Samples(arguments, 5, self._display, self._lock, trigger_patch)
        self._patch = Patch(arguments,
                            self._available_masks,
                            self._samples,
                            self._display,
                            self._lock,
                            trigger_patch,
                            self._config_tools,
                            self._tk_vars)
 </s> add         self._dispatcher = Dispatcher(self)
        self._display = FacesDisplay(self, 256, 64)
        self._samples = Samples(self, arguments, 5)
        self._patch = Patch(self, arguments) </s> remove             self._patch.current_config = self._config_tools.config
        self._patch.trigger.set()
        logger.trace("Refreshed swapped faces")  # type: ignore
 </s> add         self._dispatcher.set_needs_patch()
        logger.debug("Patched swapped faces") </s> remove         logger.trace("Refreshing swapped faces. args: %s", args)  # type: ignore
        self._tk_vars["busy"].set(True)
 </s> add         logger.debug("Patching swapped faces. args: %s", args)
        self._dispatcher.set_busy() </s> remove     def _available_masks(self) -> List[str]:
        """ list: The mask names that are available for every face in the alignments file """
        retval = [key
                  for key, val in self._samples.alignments.mask_summary.items()
                  if val == self._samples.alignments.faces_count]
        return retval
 </s> add     def config_tools(self) -> "ConfigTools":
        """ :class:`ConfigTools`: The object responsible for parsing configuration options and
        updating to/from the GUI """
        return self._config_tools

    @property
    def dispatcher(self) -> "Dispatcher":
        """ :class:`Dispatcher`: The object responsible for triggering events and variables and
        handling global GUI state """
        return self._dispatcher

    @property
    def display(self) -> FacesDisplay:
        """ :class:`~tools.preview.viewer.FacesDisplay`: The object that holds the sample,
        converted and patched faces """
        return self._display

    @property
    def lock(self) -> Lock:
        """ :class:`threading.Lock`: The threading lock object for the Preview GUI """
        return self._lock

    @property
    def progress_bar(self) -> "BusyProgressBar":
        """ :class:`~tools.preview.control_panels.BusyProgressBar`: The progress bar that indicates
        a swap/patch thread is running """
        assert self._cli_frame is not None
        return self._cli_frame.busy_progress_bar

    def update_display(self):
        """ Update the images in the canvas and redraw """
        if not hasattr(self, "_image_canvas"):  # On first call object not yet created
            return
        assert self._image_canvas is not None
        self._image_canvas.reload()
        self._cli_frame = ActionFrame(self, options_frame)
          self._image_canvas = ImagesCanvas(container, self._tk_vars)
          container.add(self._image_canvas, weight=3)
  
          options_frame = ttk.Frame(container)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
        self._cli_frame = ActionFrame(self, options_frame)
          self._opts_book = OptionsBook(options_frame,
                                        self._config_tools,
                                        self._refresh)
          container.add(options_frame, weight=1)
          self.update_idletasks()
keep keep keep keep replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 bugfix:
    - preview tool - Prevent tkinter variables from exiting main thread
refactor:
    - preview tool - Split module to smaller sub-modules + docs, locales
Typing:
    - tools.preview.cli
Unit test:
    - tools.preview.viewer 
tools/preview/preview.py