class_docstr: A frame to get sampling settings and to calculate samples for a
  CellProfiler module.
  
  A few important data members and data structures are maintained:
  'self.__module': the module selected by the user.
  'self.__pipeline': the pipeline containing the module.
  'self.__parameters_list': a list of all unbounded parameters that will
      be displayed to the user. Eg,
          [(label_1, m_1), (label_2, m_2), ...], where
          parameter i maps to the tuple (label_i, m_i) with 'label_i' its
          text label and 'm_i' its 1-based index in 'self.__module'.
  'self.__parameters_to_widgets_list': a list of lists representing the
      mapping from parameters (see above), to 'groups of widgets'. Ie,
      some parameters will have more than two values and, hence, will
      have two groups of widgets. Eg,
          [[0, 1], [[2]], [3, 4], represents a mapping
          param 0 -> widget group 0 and 1, etc.
      In practice, each such mapping will correspond exactly to a
      check box and one or more rows of spin controls, depending on how
      many values a particular parameter takes.
  'self.__sample_list': a list of samples of the selected parameters. Eg,
      suppose that parameters p_1, p_2 and p_3 are selected, then a list
      of the form
          [[s_11, s_21, s_31], [s_12, s_22, s_32], ...]
      will be returned.
      NB: for those parameters displayed, but not selected by the user, this
      list will contain the value 'None'. Eg, suppose p2 is deselected, then
      the list will have the form:
          [[s_11, None, s_31], [s_12, None, s_32], ...].
code: def on_button(self, event):
      if event.GetId() == ID_SAMPLE_BUTTON:
          # try:
          self.validate_input_ranges()
          number = self.calc_number_samples()
  
          sample_dialog = wx.MessageDialog(
              self,
              "Proceed with calculating " + str(number) + " samples?",
              caption="Confirm sample size",
              style=wx.ICON_QUESTION | wx.OK | wx.CANCEL,
          )
          if sample_dialog.ShowModal() == wx.ID_OK:
              save_dialog = wx.FileDialog(
                  event.GetEventObject(),
                  message="Save sampled output",
                  wildcard="Tab separated values (*.tsv)|*.tsv",
                  style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
              )
              if save_dialog.ShowModal() == wx.ID_OK:
                  # 1. Copy original parameter values
                  original_values = []
                  for setting in self.__module.visible_settings():
                      original_values.append(setting.get_value())
  
                  # 2. Sample input parameters
                  self.__sample_list = self.generate_parameter_samples()
  
                  # 3. Open output file and write headers
                  output_file = open(save_dialog.GetPath(), "w")
                  headers = ""
                  for i, setting in enumerate(
                      self.__module.visible_settings()
                  ):
                      # Do not write settings without values, ie, buttons etc
                      if setting.get_text() != "":
                          headers += setting.get_text()
                          if (
                              i
                              < len(self.__module.visible_settings())
                              - 1
                          ):
                              headers += "\t"
                  headers += "\n"
                  output_file.write(headers)
  
                  # 4. Run pipeline once for each sample
  
                  # ~*~
                  self.Show(False)
                  progressDialog = wx.ProgressDialog(
                      parent=self,
                      title="Sampling parameters",
                      message="Run 1",
                      maximum=len(self.__sample_list),
                  )
                  size = progressDialog.GetSize()
                  size.SetWidth(2 * size.GetWidth())
                  progressDialog.SetSize(size)
                  # ~^~
  
                  for i, l in enumerate(self.__sample_list):
                      # print '\nStarting run ' + str(i+1) + '...'
  
                      for j, value in enumerate(l):
                          if value is not None:
                              setting_nr = self.__parameters_list[j][1]
                              setting = self.__module.setting(
                                  setting_nr
                              )
                              setting.set_value(value)
                              # print str(setting.get_text()) + ' -> ' + str(setting.get_value())
  
                      # ~*~
                      progressDialog.Update(
                          i + 1,
                          newmsg="Executing run "
                          + str(i + 1)
                          + " of "
                          + str(len(self.__sample_list)),
                      )
                      # ~^~
  
                      # It's not very efficient to run the complete pipeline
                      # when only the last module's parameter values are
                      # different. However, this is the only way I can get
                      # the images to update correctly for the last module. Ie,
                      # when I don't prepare and run the pipeline from scratch
                      # for every different configuration of the last module,
                      # I get the images generated by the first run for every
                      # run.
  
                      # 4.1 Prepare to run pipeline
                      self.prepare_for_run()
                      # 4.2 Run modules
                      for module in self.__pipeline.modules():
                          if (
                              module.get_module_num()
                              <= self.__module.get_module_num()
                          ):
                              self.run_module(module)
                      # 4.3 Save output
                      self.save_run_output(
                          i, save_dialog.GetDirectory(), output_file
                      )
  
                      # This is the way to run headless, if only I could get at the images...
                      # self.stop_now = False
                      # running_pipeline = self.__pipeline.run_with_yield(
                      #    run_in_background=False,
                      #    status_callback=self.status_callback)
                      # while not self.stop_now:
                      #    measurements = running_pipeline.next()
  
                      # print '...run completed.'
                  # 5. Close output file
                  output_file.close()
  
                  # ~*~
                  progressDialog.Destroy()
                  # ~^~
  
                  # 6. Set parameters back to original values and close window
                  for i, setting in enumerate(
                      self.__module.visible_settings()
                  ):
                      setting.set_value(original_values[i])
                  self.Close(True)
  
imports: import os
  import traceback
  import numpy
  import skimage.io
  import wx
  import wx.lib.agw.floatspin
  from cellprofiler_core.image import ImageSetList
  from cellprofiler_core.measurement import Measurements
  from cellprofiler_core.object import ObjectSet
  from cellprofiler_core.pipeline import RunException
  from cellprofiler_core.setting import Binary, Divider, FigureSubscriber, Measurement, ValidationError
  from cellprofiler_core.setting.choice import Choice
  from cellprofiler_core.setting.do_something import DoSomething, RemoveSettingButton
  from cellprofiler_core.setting.range import FloatRange, IntegerRange, IntegerOrUnboundedRange
  from cellprofiler_core.setting.subscriber import ImageSubscriber
  from cellprofiler_core.setting.text import Float, Integer, Name, Text, ImageName
  import cellprofiler.gui._workspace_model
label_desc: Use image node_id 23 for calling the ParameterSampleFrame obj's underlying member method code with example usage: obj.on_button(event) without return types