Ejemplo n.º 1
0
 def default_traits_view(self):
     return View(
         VGroup(Item('blank_file'), label="Autofluorescence"),
         VGroup(Item('fsc_channel',
                     editor=EnumEditor(name='_blank_exp_channels'),
                     label="Forward Scatter Channel"),
                Item('ssc_channel',
                     editor=EnumEditor(name='_blank_exp_channels'),
                     label="Side Scatter Channel"),
                label="Morphology"),
         VGroup(Item("channels",
                     editor=CheckListEditor(cols=2,
                                            name='_blank_exp_channels'),
                     style='custom'),
                label="Channels To Calibrate",
                show_labels=False),
         VGroup(Item('bleedthrough_list',
                     editor=VerticalListEditor(editor=InstanceEditor(
                         view=self.bleedthrough_traits_view()),
                                               style='custom',
                                               mutable=False),
                     style='custom'),
                label="Bleedthrough Correction",
                show_border=False,
                show_labels=False),
         VGroup(Item('beads_name',
                     editor=EnumEditor(name='handler.beads_name_choices'),
                     label="Beads",
                     width=-125),
                Item('beads_file'),
                Item('units_list',
                     editor=VerticalListEditor(editor=InstanceEditor(
                         view=self.unit_traits_view()),
                                               style='custom',
                                               mutable=False),
                     style='custom',
                     label="Bead\nunits"),
                Item('bead_peak_quantile', label="Peak\nQuantile"),
                Item('bead_brightness_threshold', label="Peak\nThreshold "),
                Item('bead_brightness_cutoff', label="Peak\nCutoff"),
                label="Bead Calibration",
                show_border=False),
         VGroup(Item('do_color_translation',
                     label="Do color translation?",
                     editor=ToggleButtonEditor(),
                     show_label=False),
                Item('to_channel',
                     editor=EnumEditor(name='channels'),
                     visible_when='do_color_translation == True'),
                Item('mixture_model',
                     label="Use mixture\nmodel?",
                     visible_when='do_color_translation == True'),
                VGroup(Item('translation_list',
                            editor=VerticalListEditor(editor=InstanceEditor(
                                view=self.translation_traits_view()),
                                                      style='custom',
                                                      mutable=False),
                            style='custom'),
                       show_labels=False,
                       visible_when='do_color_translation == True'),
                label="Color Translation",
                show_border=False),
         VGroup(Item('status', style='readonly'),
                Item('output_directory'),
                Item('do_estimate',
                     editor=ButtonEditor(value=True,
                                         label="Estimate parameters"),
                     show_label=False),
                Item('handler.do_convert',
                     editor=ButtonEditor(value=True,
                                         label="Convert files..."),
                     enabled_when="valid_model == True",
                     show_label=False),
                label="Output",
                show_border=False),
         Item('do_exit',
              editor=ButtonEditor(value=True, label="Return to Cytoflow"),
              show_label=False), shared_op_traits)
Ejemplo n.º 2
0
 def trait_view(self, name=None, view_elements=None):
     if name is None or name=='full':
         return View(
           VGroup( 
             HSplit(
                   VSplit(
                     Item('function_search',
                          editor = InstanceEditor(view=function_search_view),
                          label      = 'Search',
                          id         = 'search',
                          style      = 'custom',
                          dock       = 'horizontal',
                          show_label = False,                      
                     ),
                     Item('html_window',
                          style='custom',
                          show_label=False,
                          springy= True,
                          resizable=True,
                     ),
                     id='search_help_view'
                   ),      
                 VSplit(
                     Item( 'object.project.active_experiment.canvas',
                           label      = 'Canvas',
                           id         = 'canvas',
                           # FIXME:  need a new way to control the canvas
                           # not using BlockEditor
                           editor     = BlockEditor(),
                           dock       = 'horizontal',
                           show_label = False
                     ),
                     Item( 'object.project.active_experiment.exec_model.code',
                           label      = 'Code',
                           id         = 'code',
                           editor     = CodeEditor(dim_lines = 'dim_lines',
                                                   dim_color = 'dim_color',
                                                   squiggle_lines = 'squiggle_lines'),
                           dock       = 'horizontal',
                           show_label = False
                     ),
                 ),
                 Item( 'context_viewer',
                       label = 'Context',
                       id = 'context_table',
                       editor = InstanceEditor(),
                       style = 'custom',
                       dock = 'horizontal',
                       show_label = False,
                 ),
                 id='panel_split',
             ),
             Item( 'status',
                   style      = 'readonly',
                   show_label = False,
                   resizable  = False 
             ),
           ),
           title     = 'Block Canvas',
           menubar   = BlockApplicationMenuBar,
           width     = 1024,
           height    = 768,
           id        = 'blockcanvas.app.application',
           resizable = True,
           handler   = BlockApplicationViewHandler(model=self),
           key_bindings = KeyBindings(
             KeyBinding(binding1='F5', method_name='_on_execute'),
             ),
         )
     elif name == 'simple':
         return View( 
                     HSplit(
                             VSplit(
                                     Item('function_search',
                                          editor = InstanceEditor(view=function_search_view),
                                          label      = 'Search',
                                          id         = 'search',
                                          style      = 'custom',
                                          dock       = 'horizontal',
                                          show_label = False),
                                     Item('html_window',
                                          style='custom',
                                          show_label=False,
                                          springy= True,
                                          resizable=True),
                                     id='search_help_view'
                                     ),      
                               Item( 'object.project.active_experiment.canvas',
                                           label      = 'Canvas',
                                           id         = 'canvas',
                                           # FIXME:  need a new way to control the canvas
                                           # not using BlockEditor
                                           editor     = BlockEditor(),
                                           dock       = 'horizontal',
                                           show_label = False),
                             id='panel_split'),
                   title     = 'Block Canvas - Simple View',
                   menubar   = BlockApplicationMenuBar,
                   width     = 800,
                   height    = 600,
                   id        = 'blockcanvas.app.application.simple',
                   resizable = True,
                   handler   = BlockApplicationViewHandler(model=self),
                   key_bindings = KeyBindings(
                                              KeyBinding(binding1='F5', method_name='_on_execute'),
                                              )
                 )
Ejemplo n.º 3
0
    def traits_view(self):
        ctrl_grp = VGroup(Item('path', show_label=False),
                        Item('highlight_bands', editor=ListEditor(mutable=False,
                                                                 style='custom', editor=InstanceEditor()))
                        )
        v = View(
               ctrl_grp,
               Item('container', show_label=False,
                       editor=ComponentEditor()),
#
                 title='Color Inspector',
                 resizable=True,
                 height=800,
                 width=900
                 )
        return v
#    def traits_view(self):
#        lgrp = VGroup(Item('low'),
#                      Item('low', show_label=False, editor=RangeEditor(mode='slider', low=0, high_name='high')))
#        hgrp = VGroup(Item('high'),
#                      Item('high', show_label=False, editor=RangeEditor(mode='slider', low_name='low', high=255)))
#        savegrp = HGroup(Item('save_button', show_label=False),
#                         Item('save_mode', show_label=False))
#        ctrlgrp = VGroup(
#                         Item('path', show_label=False),
#                         HGroup(Item('use_threshold'), Item('contrast_equalize'),
#                                HGroup(Item('contrast_low'), Item('contrast_high'), enabled_when='contrast_equalize'),
#                                Item('histogram_equalize')
#                                ),
#                         HGroup(Item('highlight'), Item('highlight_threshold')),
#                         HGroup(spring,
#                                lgrp,
#                                hgrp,
#                                VGroup(savegrp,
#                                       Item('calc_area_value', label='Calc. Area For.',
#                                                     tooltip='Calculate %area for all pixels with this value'
#                                                     ),
#                                       Item('calc_area_threshold', label='Threshold +/- px',
#                                            tooltip='bandwidth= calc_value-threshold to calc_value+threshold'
#                                            )
#
#                                       )
#                                ),
#                         HGroup(spring, Item('area', style='readonly', width= -200)),
#                         HGroup(
#                                Item('colormap_name_1', show_label=False,
#                                      editor=EnumEditor(values=color_map_name_dict.keys())),
#                                spring,
#                                Item('colormap_name_2', show_label=False,
#                                     editor=EnumEditor(values=color_map_name_dict.keys()))),
#                       )
#        v = View(ctrlgrp,
#                 Item('container', show_label=False,
#                       editor=ComponentEditor()),
#
#                 title='Color Inspector',
#                 resizable=True,
#                 height=800,
#                 width=900
#
#                 )
        return v
Ejemplo n.º 4
0
class SwiftConsole(HasTraits):
    """Traits-defined Swift Console.

    link : object
      Serial driver
    update : bool
      Update the firmware
    log_level_filter : str
      Syslog string, one of "ERROR", "WARNING", "INFO", "DEBUG".
    skip_settings : bool
      Don't read the device settings. Set to False when the console is reading
      from a network connection only.

    """

    link = Instance(sbpc.Handler)
    console_output = Instance(OutputList())
    python_console_env = Dict
    device_serial = Str('')
    dev_id = Str('')
    tracking_view = Instance(TrackingView)
    solution_view = Instance(SolutionView)
    baseline_view = Instance(BaselineView)
    observation_view = Instance(ObservationView)
    networking_view = Instance(SbpRelayView)
    observation_view_base = Instance(ObservationView)
    system_monitor_view = Instance(SystemMonitorView)
    settings_view = Instance(SettingsView)
    update_view = Instance(UpdateView)
    imu_view = Instance(IMUView)
    mag_view = Instance(MagView)
    spectrum_analyzer_view = Instance(SpectrumAnalyzerView)
    skylark_view = Instance(SkylarkView)
    log_level_filter = Enum(list(SYSLOG_LEVELS.itervalues()))
    """"
  mode : baseline and solution view - SPP, Fixed or Float
  num_sat : baseline and solution view - number of satellites
  port : which port is Swift Device is connected to
  directory_name : location of logged files
  json_logging : enable JSON logging
  csv_logging : enable CSV logging

  """

    mode = Str('')
    num_sats = Int(0)
    cnx_desc = Str('')
    latency = Str('')
    uuid = Str('')
    directory_name = Directory
    json_logging = Bool(True)
    csv_logging = Bool(False)
    cnx_icon = Str('')
    heartbeat_count = Int()
    last_timer_heartbeat = Int()
    solid_connection = Bool(False)

    csv_logging_button = SVGButton(
        toggle=True,
        label='CSV log',
        tooltip='start CSV logging',
        toggle_tooltip='stop CSV logging',
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        orientation='vertical',
        width=2,
        height=2,
    )
    json_logging_button = SVGButton(
        toggle=True,
        label='JSON log',
        tooltip='start JSON logging',
        toggle_tooltip='stop JSON logging',
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        orientation='vertical',
        width=2,
        height=2,
    )
    paused_button = SVGButton(
        label='',
        tooltip='Pause console update',
        toggle_tooltip='Resume console update',
        toggle=True,
        filename=resource_filename('console/images/iconic/pause.svg'),
        toggle_filename=resource_filename('console/images/iconic/play.svg'),
        width=8,
        height=8)
    clear_button = SVGButton(
        label='',
        tooltip='Clear console buffer',
        filename=resource_filename('console/images/iconic/x.svg'),
        width=8,
        height=8)

    view = View(VSplit(
        Tabbed(Item('tracking_view', style='custom', label='Tracking'),
               Item('solution_view', style='custom', label='Solution'),
               Item('baseline_view', style='custom', label='Baseline'),
               VSplit(
                   Item('observation_view', style='custom', show_label=False),
                   Item('observation_view_base',
                        style='custom',
                        show_label=False),
                   label='Observations',
               ),
               Item('settings_view', style='custom', label='Settings'),
               Item('update_view', style='custom', label='Update'),
               Tabbed(Item('system_monitor_view',
                           style='custom',
                           label='System Monitor'),
                      Item('imu_view', style='custom', label='IMU'),
                      Item('mag_view', style='custom', label='Magnetometer'),
                      Item('networking_view',
                           label='Networking',
                           style='custom',
                           show_label=False),
                      Item('spectrum_analyzer_view',
                           label='Spectrum Analyzer',
                           style='custom'),
                      label='Advanced',
                      show_labels=False),
               Item('skylark_view', style='custom', label='Skylark'),
               show_labels=False),
        VGroup(
            VGroup(
                HGroup(
                    Spring(width=4, springy=False),
                    Item('paused_button',
                         show_label=False,
                         padding=0,
                         width=8,
                         height=8),
                    Item('clear_button', show_label=False, width=8, height=8),
                    Item('', label='Console Log', emphasized=True),
                    Item('csv_logging_button',
                         emphasized=True,
                         show_label=False,
                         width=12,
                         height=-30,
                         padding=0),
                    Item('json_logging_button',
                         emphasized=True,
                         show_label=False,
                         width=12,
                         height=-30,
                         padding=0),
                    Item(
                        'directory_name',
                        show_label=False,
                        springy=True,
                        tooltip=
                        'Choose location for file logs. Default is home/SwiftNav.',
                        height=-25,
                        enabled_when='not(json_logging or csv_logging)',
                        editor_args={'auto_set': True}),
                    UItem(
                        'log_level_filter',
                        style='simple',
                        padding=0,
                        height=8,
                        show_label=True,
                        tooltip=
                        'Show log levels up to and including the selected level of severity.\nThe CONSOLE log level is always visible.'
                    ),
                ),
                Item('console_output',
                     style='custom',
                     editor=InstanceEditor(),
                     height=125,
                     show_label=False,
                     full_size=True),
            ),
            HGroup(
                Spring(width=4, springy=False),
                Item('',
                     label='Interface:',
                     emphasized=True,
                     tooltip='Interface for communicating with Swift device'),
                Item('cnx_desc', show_label=False, style='readonly'),
                Item('',
                     label='FIX TYPE:',
                     emphasized=True,
                     tooltip='Device Mode: SPS, Float RTK, Fixed RTK'),
                Item('mode', show_label=False, style='readonly'),
                Item('',
                     label='#Sats:',
                     emphasized=True,
                     tooltip='Number of satellites used in solution'),
                Item('num_sats', padding=2, show_label=False,
                     style='readonly'),
                Item('',
                     label='Base Latency:',
                     emphasized=True,
                     tooltip='Corrections latency (-1 means no corrections)'),
                Item('latency', padding=2, show_label=False, style='readonly'),
                Item('',
                     label='Device UUID:',
                     emphasized=True,
                     tooltip='Universally Unique Device Identifier (UUID)'),
                Item('uuid',
                     padding=2,
                     show_label=False,
                     style='readonly',
                     width=6),
                Spring(springy=True),
                Item('cnx_icon',
                     show_label=False,
                     padding=0,
                     width=8,
                     height=8,
                     visible_when='solid_connection',
                     springy=False,
                     editor=ImageEditor(
                         allow_clipping=False,
                         image=ImageResource(
                             resource_filename(
                                 'console/images/iconic/arrows_blue.png')))),
                Item('cnx_icon',
                     show_label=False,
                     padding=0,
                     width=8,
                     height=8,
                     visible_when='not solid_connection',
                     springy=False,
                     editor=ImageEditor(
                         allow_clipping=False,
                         image=ImageResource(
                             resource_filename(
                                 'console/images/iconic/arrows_grey.png')))),
                Spring(width=4, height=-2, springy=False),
            ),
            Spring(height=1, springy=False),
        ),
    ),
                icon=icon,
                resizable=True,
                width=800,
                height=600,
                handler=ConsoleHandler(),
                title=CONSOLE_TITLE)

    def print_message_callback(self, sbp_msg, **metadata):
        try:
            encoded = sbp_msg.payload.encode('ascii', 'ignore')
            for eachline in reversed(encoded.split('\n')):
                self.console_output.write_level(
                    eachline, str_to_log_level(eachline.split(':')[0]))
        except UnicodeDecodeError as e:
            print("Error encoding msg_print: {}".format(e))

    def log_message_callback(self, sbp_msg, **metadata):
        encoded = sbp_msg.text
        for eachline in reversed(encoded.split('\n')):
            self.console_output.write_level(eachline, sbp_msg.level)

    def ext_event_callback(self, sbp_msg, **metadata):
        e = MsgExtEvent(sbp_msg)
        print(
            'External event: %s edge on pin %d at wn=%d, tow=%d, time qual=%s'
            % ("Rising" if
               (e.flags &
                (1 << 0)) else "Falling", e.pin, e.wn, e.tow, "good" if
               (e.flags & (1 << 1)) else "unknown"))

    def cmd_resp_callback(self, sbp_msg, **metadata):
        r = MsgCommandResp(sbp_msg)
        print("Received a command response message with code {0}".format(
            r.code))

    def _paused_button_fired(self):
        self.console_output.paused = not self.console_output.paused

    def _log_level_filter_changed(self):
        """
        Takes log level enum and translates into the mapped integer.
        Integer stores the current filter value inside OutputList.
        """
        self.console_output.log_level_filter = str_to_log_level(
            self.log_level_filter)

    def _clear_button_fired(self):
        self.console_output.clear()

    def _directory_name_changed(self):
        if self.baseline_view and self.solution_view:
            self.baseline_view.directory_name_b = self.directory_name
            self.solution_view.directory_name_p = self.directory_name
            self.solution_view.directory_name_v = self.directory_name
        if self.observation_view and self.observation_view_base:
            self.observation_view.dirname = self.directory_name
            self.observation_view_base.dirname = self.directory_name

    def check_heartbeat(self):
        # if our heartbeat hasn't changed since the last timer interval the connection must have dropped
        if self.heartbeat_count == self.last_timer_heartbeat:
            self.solid_connection = False
        else:
            self.solid_connection = True
        self.last_timer_heartbeat = self.heartbeat_count

    def update_on_heartbeat(self, sbp_msg, **metadata):
        self.heartbeat_count += 1
        # First initialize the state to nothing, if we can't update, it will be none
        temp_mode = "None"
        temp_num_sats = 0
        view = None
        if self.baseline_view and self.solution_view:
            # If we have a recent baseline update, we use the baseline info
            if time.time() - self.baseline_view.last_btime_update < 10:
                view = self.baseline_view
            # Otherwise, if we have a recent SPP update, we use the SPP
            elif time.time() - self.solution_view.last_stime_update < 10:
                view = self.solution_view
            if view:
                if view.last_soln:
                    # if all is well we update state
                    temp_mode = mode_dict.get(get_mode(view.last_soln),
                                              EMPTY_STR)
                    temp_num_sats = view.last_soln.n_sats

        self.mode = temp_mode
        self.num_sats = temp_num_sats

        if self.settings_view:  # for auto populating surveyed fields
            self.settings_view.lat = self.solution_view.latitude
            self.settings_view.lon = self.solution_view.longitude
            self.settings_view.alt = self.solution_view.altitude
        if self.system_monitor_view:
            if self.system_monitor_view.msg_obs_window_latency_ms != -1:
                self.latency = "{0} ms".format(
                    self.system_monitor_view.msg_obs_window_latency_ms)
            else:
                self.latency = EMPTY_STR

    def _csv_logging_button_action(self):
        if self.csv_logging and self.baseline_view.logging_b and self.solution_view.logging_p and self.solution_view.logging_v:
            print("Stopped CSV logging")
            self.csv_logging = False
            self.baseline_view.logging_b = False
            self.solution_view.logging_p = False
            self.solution_view.logging_v = False

        else:
            print("Started CSV logging at %s" % self.directory_name)
            self.csv_logging = True
            self.baseline_view.logging_b = True
            self.solution_view.logging_p = True
            self.solution_view.logging_v = True

    def _start_json_logging(self, override_filename=None):
        if override_filename:
            filename = override_filename
        else:
            filename = time.strftime("swift-gnss-%Y%m%d-%H%M%S.sbp.json")
            filename = os.path.normpath(
                os.path.join(self.directory_name, filename))
        self.logger = s.get_logger(True, filename, self.expand_json)
        self.forwarder = sbpc.Forwarder(self.link, self.logger)
        self.forwarder.start()
        if self.settings_view:
            self.settings_view._settings_read_button_fired()

    def _stop_json_logging(self):
        fwd = self.forwarder
        fwd.stop()
        self.logger.flush()
        self.logger.close()

    def _json_logging_button_action(self):
        if self.first_json_press and self.json_logging:
            print(
                "JSON Logging initiated via CMD line.  Please press button again to stop logging"
            )
        elif self.json_logging:
            self._stop_json_logging()
            self.json_logging = False
            print("Stopped JSON logging")
        else:
            self._start_json_logging()
            self.json_logging = True
        self.first_json_press = False

    def _json_logging_button_fired(self):
        if not os.path.exists(self.directory_name) and not self.json_logging:
            confirm_prompt = CallbackPrompt(
                title="Logging directory creation",
                actions=[ok_button],
                callback=self._json_logging_button_action)
            confirm_prompt.text = "\nThe selected logging directory does not exist and will be created."
            confirm_prompt.run(block=False)
        else:
            self._json_logging_button_action()

    def _csv_logging_button_fired(self):
        if not os.path.exists(self.directory_name) and not self.csv_logging:
            confirm_prompt = CallbackPrompt(
                title="Logging directory creation",
                actions=[ok_button],
                callback=self._csv_logging_button_action)
            confirm_prompt.text = "\nThe selected logging directory does not exist and will be created."
            confirm_prompt.run(block=False)
        else:
            self._csv_logging_button_action()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.console_output.close()

    def __init__(self,
                 link,
                 update,
                 log_level_filter,
                 skip_settings=False,
                 error=False,
                 cnx_desc=None,
                 json_logging=False,
                 log_dirname=None,
                 override_filename=None,
                 log_console=False,
                 networking=None,
                 connection_info=None,
                 expand_json=False):
        self.error = error
        self.cnx_desc = cnx_desc
        self.connection_info = connection_info
        self.dev_id = cnx_desc
        self.num_sats = 0
        self.mode = ''
        self.forwarder = None
        self.latency = '--'
        self.expand_json = expand_json
        # if we have passed a logfile, we set our directory to it
        override_filename = override_filename

        if log_dirname:
            self.directory_name = log_dirname
            if override_filename:
                override_filename = os.path.join(log_dirname,
                                                 override_filename)
        else:
            self.directory_name = swift_path

        # Start swallowing sys.stdout and sys.stderr
        self.console_output = OutputList(tfile=log_console,
                                         outdir=self.directory_name)
        sys.stdout = self.console_output
        self.console_output.write("Console: " + CONSOLE_VERSION +
                                  " starting...")
        if not error:
            sys.stderr = self.console_output

        self.log_level_filter = log_level_filter
        self.console_output.log_level_filter = str_to_log_level(
            log_level_filter)
        try:
            self.link = link
            self.link.add_callback(self.print_message_callback,
                                   SBP_MSG_PRINT_DEP)
            self.link.add_callback(self.log_message_callback, SBP_MSG_LOG)
            self.link.add_callback(self.ext_event_callback, SBP_MSG_EXT_EVENT)
            self.link.add_callback(self.cmd_resp_callback,
                                   SBP_MSG_COMMAND_RESP)
            self.link.add_callback(self.update_on_heartbeat, SBP_MSG_HEARTBEAT)
            self.dep_handler = DeprecatedMessageHandler(link)
            settings_read_finished_functions = []
            self.tracking_view = TrackingView(self.link)
            self.solution_view = SolutionView(self.link,
                                              dirname=self.directory_name)
            self.baseline_view = BaselineView(self.link,
                                              dirname=self.directory_name)
            self.observation_view = ObservationView(
                self.link,
                name='Local',
                relay=False,
                dirname=self.directory_name)
            self.observation_view_base = ObservationView(
                self.link,
                name='Remote',
                relay=True,
                dirname=self.directory_name)
            self.system_monitor_view = SystemMonitorView(self.link)
            self.update_view = UpdateView(self.link,
                                          download_dir=swift_path,
                                          prompt=update,
                                          connection_info=self.connection_info)
            self.imu_view = IMUView(self.link)
            self.mag_view = MagView(self.link)
            self.spectrum_analyzer_view = SpectrumAnalyzerView(self.link)
            settings_read_finished_functions.append(
                self.update_view.compare_versions)
            if networking:
                from ruamel.yaml import YAML
                yaml = YAML(typ='safe')
                try:
                    networking_dict = yaml.load(networking)
                    networking_dict.update({'show_networking': True})
                except yaml.YAMLError:
                    print(
                        "Unable to interpret networking cmdline argument.  It will be ignored."
                    )
                    import traceback
                    print(traceback.format_exc())
                    networking_dict = {'show_networking': True}
            else:
                networking_dict = {}
            networking_dict.update(
                {'whitelist': [SBP_MSG_POS_LLH, SBP_MSG_HEARTBEAT]})
            self.networking_view = SbpRelayView(self.link, **networking_dict)
            self.skylark_view = SkylarkView()
            self.json_logging = json_logging
            self.csv_logging = False
            self.first_json_press = True
            if json_logging:
                self._start_json_logging(override_filename)
                self.json_logging = True
            # we set timer interval to 1200 milliseconds because we expect a heartbeat each second
            self.timer_cancel = call_repeatedly(1.2, self.check_heartbeat)

            # Once we have received the settings, update device_serial with
            # the Swift serial number which will be displayed in the window
            # title. This callback will also update the header route as used
            # by the networking view.

            def update_serial():
                mfg_id = None
                try:
                    self.uuid = self.settings_view.settings['system_info'][
                        'uuid'].value
                    mfg_id = self.settings_view.settings['system_info'][
                        'serial_number'].value
                except KeyError:
                    pass
                if mfg_id:
                    self.device_serial = 'PK' + str(mfg_id)
                self.skylark_view.set_uuid(self.uuid)
                self.networking_view.set_route(uuid=self.uuid,
                                               serial_id=mfg_id)
                if self.networking_view.connect_when_uuid_received:
                    self.networking_view._connect_rover_fired()

            settings_read_finished_functions.append(update_serial)
            self.settings_view = SettingsView(self.link,
                                              settings_read_finished_functions,
                                              skip=skip_settings)
            self.update_view.settings = self.settings_view.settings
            self.python_console_env = {
                'send_message': self.link,
                'link': self.link,
            }
            self.python_console_env.update(
                self.tracking_view.python_console_cmds)
            self.python_console_env.update(
                self.solution_view.python_console_cmds)
            self.python_console_env.update(
                self.baseline_view.python_console_cmds)
            self.python_console_env.update(
                self.observation_view.python_console_cmds)
            self.python_console_env.update(
                self.networking_view.python_console_cmds)
            self.python_console_env.update(
                self.system_monitor_view.python_console_cmds)
            self.python_console_env.update(
                self.update_view.python_console_cmds)
            self.python_console_env.update(self.imu_view.python_console_cmds)
            self.python_console_env.update(self.mag_view.python_console_cmds)
            self.python_console_env.update(
                self.settings_view.python_console_cmds)
            self.python_console_env.update(
                self.spectrum_analyzer_view.python_console_cmds)

        except:  # noqa
            import traceback
            traceback.print_exc()
            if self.error:
                sys.exit(1)
Ejemplo n.º 5
0
class TwoDimensionalPanel(Handler):
    images = Dict # Str -> Tuple(imgd, affine, tkr_affine)

    #original_current_image = Any #np.ndarray XxYxZ
    current_image = Any # np.ndarray XxYxZ
    #original_image is not allowed to be altered by thresholding.
    #current_image may be reset by copying original whenever threshold change
    current_affine = Any
    current_tkr_affine = Any

    xy_plane = Instance(Plot)
    xz_plane = Instance(Plot)
    yz_plane = Instance(Plot)
    
    pins = Dict # Str -> (Str -> 3-Tuple)
    pin_tolerance = DelegatesTo('info_panel')
    minimum_contrast = DelegatesTo('info_panel')
    maximum_contrast = DelegatesTo('info_panel')

    current_pin = Str(None)
    confirm_movepin_postproc_button = DelegatesTo('info_panel')
    confirm_movepin_internal_button = DelegatesTo('info_panel')
    add_electrode_button = DelegatesTo('info_panel')
    move_electrode_internally_event = Event
    move_electrode_postprocessing_event = Event
    add_electrode_event = Event
    track_cursor_button = DelegatesTo('info_panel')
    track_cursor_event = Event
    untrack_cursor_event = Event
    panel2d_closed_event = Event
    reset_image_button = DelegatesTo('info_panel')
    
    info_panel = Instance(InfoPanel, ())

    currently_showing_list = DelegatesTo('info_panel')
    currently_showing = DelegatesTo('info_panel')

    #later we will rename cursor to "coord"

    cursor = Tuple # 3-tuple

    null = Any # None

    _finished_plotting = Bool(False)

    traits_view = View(
        Group(
        HGroup(
            Item(name='xz_plane', editor=ComponentEditor(),
                height=400, width=400, show_label=False, resizable=True),
            Item(name='yz_plane', editor=ComponentEditor(),
                height=400, width=400, show_label=False, resizable=True),
        ),
        HGroup(
            Item(name='xy_plane', editor=ComponentEditor(),
                height=400, width=400, show_label=False, resizable=True),
            Item(name='info_panel', 
                    editor=InstanceEditor(), 
                style='custom',
            #Item(name='null', editor=NullEditor(),
                height=400, width=400, show_label=False, resizable=True),
        ),
        ),
        title='Contact 867-5309 for blobfish sales',
    )

    def map_cursor(self, cursor, affine, invert=False):
        x,y,z = cursor
        aff_to_use = np.linalg.inv(affine) if invert else affine
        mcursor, = apply_affine([cursor], aff_to_use)
        return tuple(map(lambda x: truncate(x, 2), mcursor))

    #def cut_data(self, data, mcursor):
    def cut_data(self, ndata, mcursor):
        xm,ym,zm = [int(np.round(c)) for c in mcursor]
        #xm, ym, zm = mcursor
        #yz_cut = np.rot90(data[xm,:,:].T)
        #xz_cut = np.rot90(data[:,ym,:].T)
        #xy_cut = np.rot90(data[:,:,zm].T)

        #ndata = data.copy()
        ndata[ndata < self.minimum_contrast] = self.minimum_contrast
        ndata[ndata > self.maximum_contrast] = self.maximum_contrast

        yz_cut = ndata[xm,:,:].T
        xz_cut = ndata[:,ym,:].T
        xy_cut = ndata[:,:,zm].T
        return xy_cut, xz_cut, yz_cut

    def load_img(self, imgf, reorient2std=False, image_name=None):
        self._finished_plotting = False

        img = nib.load(imgf)

        aff = np.dot(
            get_orig2std(imgf) if reorient2std else np.eye(4),
            img.get_affine())
        tkr_aff = np.dot(
            reorient_orig2std_tkr_mat if reorient2std else np.eye(4),
            get_vox2rasxfm(imgf, stem='vox2ras-tkr'))

        #from nilearn.image.resampling import reorder_img

        #img = reorder_img(uimg, resample='continuous')

        xsz, ysz, zsz = img.shape

        #print 'image coordinate transform', img.get_affine()

        imgd = img.get_data()
        if reorient2std:
            imgd = np.swapaxes(imgd, 1, 2)[:,:,::-1]

        #print 'image size', imgd.shape

        if image_name is None:
            from utils import gensym
            image_name = 'image%s'%gensym()

        self.images[image_name] = (imgd, aff, tkr_aff)
        self.currently_showing_list.append(
            NullInstanceHolder(name=image_name))
        self.currently_showing = self.currently_showing_list[-1]

        self.pins[image_name] = {}
        #self.current_pin = image_name

        self.show_image(image_name)

    @on_trait_change('currently_showing, minimum_contrast, maximum_contrast')
    def switch_image(self):
        self.show_image(self.currently_showing.name)

    @on_trait_change('reset_image_button')
    def center_image(self):
        x, y, z = self.current_image.shape

        for plane, (r,c) in zip((self.xy_plane, self.xz_plane, self.yz_plane),
                                ((x,y), (x,z), (y,z))):
            plane.index_mapper.range.low = 0
            plane.value_mapper.range.low = 0
            plane.index_mapper.range.high = r
            plane.value_mapper.range.high = c
    
    def show_image(self, image_name, xyz=None):
        # XYZ is given in pixel coordinates
        cur_img_t, self.current_affine, self.current_tkr_affine = (
            self.images[image_name])

        self.current_image = cur_img_t.copy()

        if xyz is None:
            xyz = tuple(np.array(self.current_image.shape) // 2)
        self.cursor = x,y,z = xyz
        xy_cut, xz_cut, yz_cut = self.cut_data(self.current_image, 
            self.cursor)

        xsz, ysz, zsz = self.current_image.shape

        xy_plotdata = ArrayPlotData()
        xy_plotdata.set_data('imagedata', xy_cut)
        xy_plotdata.set_data('cursor_x', np.array((x,)))
        xy_plotdata.set_data('cursor_y', np.array((y,)))

        xz_plotdata = ArrayPlotData()
        xz_plotdata.set_data('imagedata', xz_cut)
        xz_plotdata.set_data('cursor_x', np.array((x,)))
        xz_plotdata.set_data('cursor_z', np.array((z,)))

        yz_plotdata = ArrayPlotData()
        yz_plotdata.set_data('imagedata', yz_cut)
        yz_plotdata.set_data('cursor_y', np.array((y,)))
        yz_plotdata.set_data('cursor_z', np.array((z,)))

        self.xy_plane = Plot(xy_plotdata, bgcolor='black',
            #aspect_ratio=xsz/ysz)
            )
        self.xz_plane = Plot(xz_plotdata, bgcolor='black',
            #aspect_ratio=xsz/zsz)
            )
        self.yz_plane = Plot(yz_plotdata, bgcolor='black',
            #aspect_ratio=ysz/zsz)
            )

        self.xy_plane.img_plot('imagedata',name='brain',colormap=bone_cmap)
        self.xz_plane.img_plot('imagedata',name='brain',colormap=bone_cmap)
        self.yz_plane.img_plot('imagedata',name='brain',colormap=bone_cmap)

        self.xy_plane.plot(('cursor_x','cursor_y'), type='scatter', 
            color='red', marker='plus', size=3, name='cursor')
        self.xz_plane.plot(('cursor_x','cursor_z'), type='scatter',
            color='red', marker='plus', size=3, name='cursor')
        self.yz_plane.plot(('cursor_y','cursor_z'), type='scatter',
            color='red', marker='plus', size=3, name='cursor')

        self.xy_plane.tools.append(Click2DPanelTool(self, 'xy'))
        self.xz_plane.tools.append(Click2DPanelTool(self, 'xz'))
        self.yz_plane.tools.append(Click2DPanelTool(self, 'yz'))

        self.xy_plane.tools.append(ZoomTool( self.xy_plane ))
        self.xz_plane.tools.append(ZoomTool( self.xz_plane ))
        self.yz_plane.tools.append(ZoomTool( self.yz_plane ))

        #self.xy_plane.tools.append(PanTool( self.xy_plane ))
        #self.xz_plane.tools.append(PanTool( self.xz_plane ))
        #self.yz_plane.tools.append(PanTool( self.yz_plane ))

        self.info_panel.cursor = self.cursor
        self.info_panel.cursor_ras = self.map_cursor(self.cursor, 
            self.current_affine)
        self.info_panel.cursor_tkr = self.map_cursor(self.cursor,
            self.current_tkr_affine)
        self.info_panel.cursor_intensity = self.current_image[x,y,z]

        self._finished_plotting = True

        if image_name in self.pins:
            for pin in self.pins[image_name]:
                px, py, pz, pcolor = self.pins[image_name][pin]
                self.drop_pin(px,py,pz, name=pin, color=pcolor)

    def cursor_outside_image_dimensions(self, cursor, image=None):
        if image is None:
            image = self.current_image

        x, y, z = cursor

        x_sz, y_sz, z_sz = image.shape

        if not 0 <= x < x_sz:
            return True
        if not 0 <= y < y_sz:
            return True
        if not 0 <= z < z_sz:
            return True
        
        return False

    def move_cursor(self, x, y, z, suppress_cursor=False, suppress_ras=False,
            suppress_tkr=False):

        #it doesnt seem necessary for the instance variable cursor to exist
        #at all but this code isn't broken
        cursor = x,y,z

        if self.cursor_outside_image_dimensions(cursor):
            print ('Cursor %.2f %.2f %.2f outside image dimensions, doing '
                'nothing'%(x,y,z))
            return

        self.cursor = cursor

        xy_cut, xz_cut, yz_cut = self.cut_data(self.current_image, self.cursor)

        print 'clicked on point %.2f %.2f %.2f'%(x,y,z)

        self.xy_plane.data.set_data('imagedata', xy_cut)
        self.xz_plane.data.set_data('imagedata', xz_cut)
        self.yz_plane.data.set_data('imagedata', yz_cut)

        self.xy_plane.data.set_data('cursor_x', np.array((x,)))
        self.xy_plane.data.set_data('cursor_y', np.array((y,)))

        self.xz_plane.data.set_data('cursor_x', np.array((x,)))
        self.xz_plane.data.set_data('cursor_z', np.array((z,)))

        self.yz_plane.data.set_data('cursor_y', np.array((y,)))
        self.yz_plane.data.set_data('cursor_z', np.array((z,)))

        if not suppress_cursor:
            self.info_panel.cursor = tuple(
                map(lambda x:truncate(x, 2), self.cursor))
        if not suppress_ras:
            self.info_panel.cursor_ras = self.map_cursor(self.cursor,
                self.current_affine)
        if not suppress_tkr:
            self.info_panel.cursor_tkr = self.map_cursor(self.cursor,
                self.current_tkr_affine)
        self.info_panel.cursor_intensity = truncate(
            self.current_image[x,y,z],3)

        image_name = self.currently_showing.name

        if image_name in self.pins:
            for pin in self.pins[image_name]:
                px, py, pz, pcolor = self.pins[image_name][pin]
                self.drop_pin(px,py,pz, name=pin, color=pcolor)

        self.untrack_cursor_event = True

    def redraw(self):
        self.xz_plane.request_redraw()
        self.yz_plane.request_redraw()
        self.xy_plane.request_redraw()

    def drop_pin(self, x, y, z, name='pin', color='yellow', 
            image_name=None, ras_coords=False, alter_current_pin=True):
        '''
        XYZ is given in pixel space
        '''
        if ras_coords:
            #affine might not necessarily be from image currently on display
            _,_,affine = self.images[image_name]

            ras_pin = self.map_cursor((x,y,z), affine, invert=True)
            x,y,z = ras_pin

        if image_name is None:
            image_name = self.currently_showing.name

        cx, cy, cz = self.cursor

        tolerance = self.pin_tolerance

        if image_name == self.currently_showing.name:
            self.xy_plane.data.set_data('%s_x'%name, 
                np.array((x,) if np.abs(z - cz) < tolerance else ()))
            self.xy_plane.data.set_data('%s_y'%name, 
                np.array((y,) if np.abs(z - cz) < tolerance else ()))
            
            self.xz_plane.data.set_data('%s_x'%name, 
                np.array((x,) if np.abs(y - cy) < tolerance else ()))
            self.xz_plane.data.set_data('%s_z'%name, 
                np.array((z,) if np.abs(y - cy) < tolerance else ()))
        
            self.yz_plane.data.set_data('%s_y'%name, 
                np.array((y,) if np.abs(x - cx) < tolerance else ()))
            self.yz_plane.data.set_data('%s_z'%name, 
                np.array((z,) if np.abs(x - cx) < tolerance else ()))

            #if name not in self.pins[image_name]:
            if name not in self.xy_plane.plots:
                self.xy_plane.plot(('%s_x'%name,'%s_y'%name), type='scatter', 
                    color=color, marker='dot', size=3, name=name)
                self.xz_plane.plot(('%s_x'%name,'%s_z'%name), type='scatter',
                    color=color, marker='dot', size=3, name=name)
                self.yz_plane.plot(('%s_y'%name,'%s_z'%name), type='scatter',
                    color=color, marker='dot', size=3, name=name)

            self.redraw()

        self.pins[image_name][name] = (x,y,z,color)

        if alter_current_pin:
            self.current_pin = name

    def move_mouse(self, x, y, z):
        mouse = (x,y,z)

        if self.cursor_outside_image_dimensions(mouse):
            return

        self.info_panel.mouse = tuple(map(lambda x:truncate(x, 2), mouse))
        self.info_panel.mouse_ras = self.map_cursor(mouse,
            self.current_affine)
        self.info_panel.mouse_tkr = self.map_cursor(mouse, 
            self.current_tkr_affine)
        self.info_panel.mouse_intensity = truncate(
            self.current_image[x,y,z], 3)

    def _confirm_movepin_internal_button_fired(self):
        self.move_electrode_internally_event = True

    def _confirm_movepin_postproc_button_fired(self):
        self.move_electrode_postprocessing_event = True 

    def _add_electrode_button_fired(self):
        self.add_electrode_event = True

    def _track_cursor_button_fired(self):
        self.track_cursor_event = True

    def closed(self, info, is_ok):
        self.panel2d_closed_event = True

    #because these calls all call map_cursor, which changes the listener
    #variables they end up infinite looping.

    #to solve this we manage _finished_plotting manually
    #so that move_cursor is only called once when any listener is triggered
    @on_trait_change('info_panel:cursor_csvlist')
    def _listen_cursor(self):
        if self._finished_plotting and len(self.info_panel.cursor) == 3:
            self._finished_plotting = False
            x,y,z = self.info_panel.cursor
            self.move_cursor(x,y,z, suppress_cursor=True)
            self._finished_plotting = True

    @on_trait_change('info_panel:cursor_ras_csvlist')
    def _listen_cursor_ras(self):
        if self._finished_plotting and len(self.info_panel.cursor_ras) == 3:
            self._finished_plotting = False
            x,y,z = self.map_cursor(self.info_panel.cursor_ras,
                self.current_affine, invert=True)
            self.move_cursor(x,y,z, suppress_ras=True)
            self._finished_plotting = True

    @on_trait_change('info_panel:cursor_tkr_csvlist')
    def _listen_cursor_tkr(self):
        if self._finished_plotting and len(self.info_panel.cursor_tkr) == 3:
            self._finished_plotting = False
            x,y,z = self.map_cursor(self.info_panel.cursor_tkr,
                self.current_tkr_affine, invert=True)
            self.move_cursor(x,y,z, suppress_tkr=True)
            self._finished_plotting = True
Ejemplo n.º 6
0
class SwiftConsole(HasTraits):
    link = Instance(serial_link.SerialLink)
    console_output = Instance(OutputStream)
    python_console_env = Dict
    a = Int
    b = Int
    tracking_view = Instance(TrackingView)
    almanac_view = Instance(AlmanacView)
    solution_view = Instance(SolutionView)
    baseline_view = Instance(BaselineView)
    observation_view = Instance(ObservationView)
    observation_view_base = Instance(ObservationView)
    system_monitor_view = Instance(SystemMonitorView)
    simulator_view = Instance(SimulatorView)
    settings_view = Instance(SettingsView)

    view = View(VSplit(
        Tabbed(Item('tracking_view', style='custom', label='Tracking'),
               Item('almanac_view', style='custom', label='Almanac'),
               Item('solution_view', style='custom', label='Solution'),
               Item('baseline_view', style='custom', label='Baseline'),
               VSplit(
                   Item('observation_view', style='custom', show_label=False),
                   Item('observation_view_base',
                        style='custom',
                        show_label=False),
                   label='Observations',
               ),
               Item('simulator_view', style='custom', label='Simulator'),
               Item('settings_view', style='custom', label='Settings'),
               Item('system_monitor_view',
                    style='custom',
                    label='System Monitor'),
               Item('python_console_env',
                    style='custom',
                    label='Python Console',
                    editor=ShellEditor()),
               show_labels=False),
        Item(
            'console_output',
            style='custom',
            editor=InstanceEditor(),
            height=0.3,
            show_label=False,
        ),
    ),
                icon=icon,
                resizable=True,
                width=900,
                height=600,
                title='Piksi Console, Version: ' + CONSOLE_VERSION)

    def print_message_callback(self, data):
        try:
            self.console_output.write(data.encode('ascii', 'ignore'))
        except UnicodeDecodeError:
            print "Oh crap!"

    def debug_var_callback(self, data):
        x = struct.unpack('<d', data[:8])[0]
        name = data[8:]
        print "VAR: %s = %d" % (name, x)

    def __init__(self, *args, **kwargs):
        try:
            update = kwargs.pop('update')
        except KeyError:
            update = True

        self.console_output = OutputStream()
        sys.stdout = self.console_output
        sys.stderr = self.console_output

        try:
            self.link = serial_link.SerialLink(*args, **kwargs)
            self.link.add_callback(ids.PRINT, self.print_message_callback)

            self.link.add_callback(ids.DEBUG_VAR, self.debug_var_callback)

            settings_read_finished_functions = []

            self.tracking_view = TrackingView(self.link)
            self.almanac_view = AlmanacView(self.link)
            self.solution_view = SolutionView(self.link)
            self.baseline_view = BaselineView(self.link)
            self.observation_view = ObservationView(self.link,
                                                    name='Rover',
                                                    relay=False)
            self.observation_view_base = ObservationView(self.link,
                                                         name='Base',
                                                         relay=True)
            self.system_monitor_view = SystemMonitorView(self.link)
            self.simulator_view = SimulatorView(self.link)
            self.settings_view = SettingsView(self.link)

            if update:
                self.ocu = OneClickUpdate(self.link, self.console_output)
                settings_read_finished_functions.append(self.ocu.start)

            self.settings_view = \
                SettingsView(self.link, settings_read_finished_functions)

            if update:
                self.ocu.point_to_settings(self.settings_view.settings)

            self.python_console_env = {
                'send_message': self.link.send_message,
                'link': self.link,
            }
            self.python_console_env.update(
                self.tracking_view.python_console_cmds)
            self.python_console_env.update(
                self.almanac_view.python_console_cmds)
            self.python_console_env.update(
                self.solution_view.python_console_cmds)
            self.python_console_env.update(
                self.baseline_view.python_console_cmds)
            self.python_console_env.update(
                self.observation_view.python_console_cmds)
            self.python_console_env.update(
                self.system_monitor_view.python_console_cmds)
        except:
            import traceback
            traceback.print_exc()

    def stop(self):
        self.link.close()
Ejemplo n.º 7
0
class Scene(TVTKScene, Widget):
    """A VTK interactor scene widget for pyface and wxPython.

    This widget uses a RenderWindowInteractor and therefore supports
    interaction with VTK widgets.  The widget uses TVTK.  In addition
    to the features that the base TVTKScene provides this widget
    supports:

    - saving the rendered scene to the clipboard.

    - picking data on screen.  Press 'p' or 'P' when the mouse is over
      a point that you need to pick.

    - The widget also uses a light manager to manage the lighting of
      the scene.  Press 'l' or 'L' to activate a GUI configuration
      dialog for the lights.

    - Pressing the left, right, up and down arrow let you rotate the
      camera in those directions.  When shift-arrow is pressed then
      the camera is panned.  Pressing the '+' (or '=')  and '-' keys
      let you zoom in and out.

    - Pressing the 'f' key will set the camera focal point to the
      current point.

    - full screen rendering via the full_screen button on the UI.

    """

    # The version of this class.  Used for persistence.
    __version__ = 0

    ###########################################################################
    # Traits.
    ###########################################################################

    # Turn on full-screen rendering.
    full_screen = Button('Full Screen')

    # The picker handles pick events.
    picker = Instance(picker.Picker)

    ########################################

    # Render_window's view.
    _stereo_view = Group(
        Item(name='stereo_render'),
        Item(name='stereo_type'),
        show_border=True,
        label='Stereo rendering',
    )

    # The default view of this object.
    default_view = View(Group(
        Group(
            Item(name='background'),
            Item(name='foreground'),
            Item(name='parallel_projection'),
            Item(name='disable_render'),
            Item(name='off_screen_rendering'),
            Item(name='jpeg_quality'),
            Item(name='jpeg_progressive'),
            Item(name='magnification'),
            Item(name='anti_aliasing_frames'),
            Item(name='full_screen', show_label=False),
        ),
        Group(
            Item(name='render_window',
                 style='custom',
                 visible_when='object.stereo',
                 editor=InstanceEditor(view=View(_stereo_view)),
                 show_label=False), ),
        label='Scene'),
                        Group(Item(name='picker',
                                   style='custom',
                                   show_label=False),
                              label='Picker'),
                        Group(Item(name='light_manager',
                                   style='custom',
                                   show_label=False),
                              label='Lights'),
                        Group(Item(name='movie_maker',
                                   style='custom',
                                   show_label=False),
                              label='Movie'),
                        buttons=['OK', 'Cancel'])

    ########################################
    # Private traits.

    _vtk_control = Instance(wxVTKRenderWindowInteractor)
    _fullscreen = Any
    _interacting = Bool

    ###########################################################################
    # 'object' interface.
    ###########################################################################
    def __init__(self, parent=None, **traits):
        """ Initializes the object. """

        # Base class constructor.
        super(Scene, self).__init__(parent, **traits)

        # Setup the default picker.
        self.picker = picker.Picker(self)

    def __get_pure_state__(self):
        """Allows us to pickle the scene."""
        # The control attribute is not picklable since it is a VTK
        # object so we remove it.
        d = super(Scene, self).__get_pure_state__()
        for x in ['_vtk_control', '_fullscreen', '_interacting']:
            d.pop(x, None)
        return d

    ###########################################################################
    # 'Scene' interface.
    ###########################################################################
    def render(self):
        """ Force the scene to be rendered. Nothing is done if the
        `disable_render` trait is set to True."""
        if not self.disable_render:
            self._vtk_control.Render()

    def get_size(self):
        """Return size of the render window."""
        return self._vtk_control.GetSize()

    def set_size(self, size):
        """Set the size of the window."""
        self._vtk_control.SetSize(size)

    def hide_cursor(self):
        """Hide the cursor."""
        self._vtk_control.HideCursor()

    def show_cursor(self):
        """Show the cursor."""
        self._vtk_control.ShowCursor()

    ###########################################################################
    # 'TVTKScene' interface.
    ###########################################################################
    def save_to_clipboard(self):
        """Saves a bitmap of the scene to the clipboard."""
        handler, name = tempfile.mkstemp()
        self.save_bmp(name)
        bmp = wx.Bitmap(name, wx.BITMAP_TYPE_BMP)
        bmpdo = wx.BitmapDataObject(bmp)
        wx.TheClipboard.Open()
        wx.TheClipboard.SetData(bmpdo)
        wx.TheClipboard.Close()
        os.close(handler)
        os.unlink(name)

    ###########################################################################
    # `wxVTKRenderWindowInteractor` interface.
    ###########################################################################
    def OnKeyDown(self, event):
        """This method is overridden to prevent the 's'/'w'/'e'/'q'
        keys from doing the default thing which is generally useless.
        It also handles the 'p' and 'l' keys so the picker and light
        manager are called.
        """
        keycode = event.GetKeyCode()
        modifiers = event.HasModifiers()
        camera = self.camera
        if keycode < 256:
            key = chr(keycode)
            if key == '-':
                camera.zoom(0.8)
                self.render()
                self._record_methods('camera.zoom(0.8)\nrender()')
                return
            if key in ['=', '+']:
                camera.zoom(1.25)
                self.render()
                self._record_methods('camera.zoom(1.25)\nrender()')
                return
            if key.lower() in ['q', 'e'] or keycode == wx.WXK_ESCAPE:
                self._disable_fullscreen()
                self.picker.close_picker()
            if key.lower() in ['w']:
                event.Skip()
                return
            if key.lower() in ['r']:
                self._record_methods('reset_zoom()')
            # Handle picking.
            if key.lower() in ['p']:
                # In wxPython-2.6, there appears to be a bug in
                # EVT_CHAR so that event.GetX() and event.GetY() are
                # not correct.  Therefore the picker is called on
                # KeyUp.
                event.Skip()
                return
            # Camera focal point.
            if key.lower() in ['f']:
                event.Skip()
                return
            # Light configuration.
            if key.lower() in ['l'] and not modifiers:
                self.light_manager.configure()
                return
            if key.lower() in ['s'] and not modifiers:
                parent = self._vtk_control.GetParent()
                fname = popup_save(parent)
                if len(fname) != 0:
                    self.save(fname)
                return

        shift = event.ShiftDown()
        if keycode == wx.WXK_LEFT:
            if shift:
                camera.yaw(-5)
                self._record_methods('camera.yaw(-5)')
            else:
                camera.azimuth(5)
                self._record_methods('camera.azimuth(5)')
            self.render()
            self._record_methods('render()')
            return
        elif keycode == wx.WXK_RIGHT:
            if shift:
                camera.yaw(5)
                self._record_methods('camera.yaw(5)')
            else:
                camera.azimuth(-5)
                self._record_methods('camera.azimuth(-5)')
            self.render()
            self._record_methods('render()')
            return
        elif keycode == wx.WXK_UP:
            if shift:
                camera.pitch(-5)
                self._record_methods('camera.pitch(-5)')
            else:
                camera.elevation(-5)
                self._record_methods('camera.elevation(-5)')
            camera.orthogonalize_view_up()
            self.render()
            self._record_methods('camera.orthogonalize_view_up()\nrender()')
            return
        elif keycode == wx.WXK_DOWN:
            if shift:
                camera.pitch(5)
                self._record_methods('camera.pitch(5)')
            else:
                camera.elevation(5)
                self._record_methods('camera.elevation(5)')
            camera.orthogonalize_view_up()
            self.render()
            self._record_methods('camera.orthogonalize_view_up()\nrender()')
            return

        self._vtk_control.OnKeyDown(event)

        # Skipping the event is not ideal but necessary because we
        # have no way of knowing of the event was really handled or
        # not and not skipping will break any keyboard accelerators.
        # In practice this does not seem to pose serious problems.
        event.Skip()

    def OnKeyUp(self, event):
        """This method is overridden to prevent the 's'/'w'/'e'/'q'
        keys from doing the default thing which is generally useless.
        It also handles the 'p' and 'l' keys so the picker and light
        manager are called.  The 'f' key sets the camera focus.
        """
        keycode = event.GetKeyCode()
        modifiers = event.HasModifiers()
        if keycode < 256:
            key = chr(keycode)
            if key.lower() in ['s', 'w', 'e', 'q']:
                event.Skip()
                return
            # Set camera focal point.
            if key.lower() in ['f']:
                if not modifiers:
                    if sys.platform == 'darwin':
                        x, y = self._interactor.event_position
                    else:
                        x = event.GetX()
                        y = self._vtk_control.GetSize()[1] - event.GetY()
                    data = self.picker.pick_world(x, y)
                    coord = data.coordinate
                    if coord is not None:
                        self.camera.focal_point = coord
                        self.render()
                        self._record_methods('camera.focal_point = %r\n'\
                                             'render()'%list(coord))
                        return
            # Handle picking.
            if key.lower() in ['p']:
                if not modifiers:
                    if sys.platform == 'darwin':
                        x, y = self._interactor.event_position
                    else:
                        x = event.GetX()
                        y = self._vtk_control.GetSize()[1] - event.GetY()
                    self.picker.pick(x, y)
                    return
                else:
                    # This is here to disable VTK's own pick handler
                    # which can get called when you press Alt/Ctrl +
                    # 'p'.
                    event.Skip()
                    return
            # Light configuration.
            if key.lower() in ['l']:
                event.Skip()
                return

        self._vtk_control.OnKeyUp(event)
        event.Skip()

    def OnPaint(self, event):
        """This method is overridden temporarily in order to create
        the light manager.  This is necessary because it makes sense
        to create the light manager only when the widget is realized.
        Only when the widget is realized is the VTK render window
        created and only then are the default lights all setup
        correctly.  This handler is removed on the first Paint event
        and the default paint handler of the
        wxVTKRenderWindowInteractor is used instead."""

        if self._vtk_control is None:
            return

        # Call the original handler (this will Show the widget)
        self._vtk_control.OnPaint(event)
        if len(self.renderer.lights) == 0:
            # The renderer is not ready yet, we do not do anything, and
            # we do not remove this callback, so that it will be called
            # later.
            return
        # Now create the light manager.
        self.light_manager = light_manager.LightManager(self)

        renwin = self._renwin
        renwin.update_traits()

        vtk_rw = tvtk.to_vtk(renwin)
        renwin.add_observer('StartEvent', messenger.send)
        messenger.connect(vtk_rw, 'StartEvent', self._start_event_callback)
        renwin.add_observer('EndEvent', messenger.send)
        messenger.connect(vtk_rw, 'EndEvent', self._end_event_callback)

        # Reset the event handler to the default since our job is done.
        self._vtk_control.Bind(wx.EVT_PAINT,
                               None)  # Remove the default handler.
        self._vtk_control.Bind(wx.EVT_PAINT, self._vtk_control.OnPaint)

    def OnSize(self, event):
        """Overrides the default OnSize in order to refresh the traits
        of the render window."""
        if self._renwin is not None:
            self._vtk_control.OnSize(event)
            self._renwin.update_traits()

    def OnButtonDown(self, event):
        """Overrides the default on button down method.
        """
        self._interacting = True
        self._vtk_control.OnButtonDown(event)

    def OnButtonUp(self, event):
        self._interacting = False
        self._vtk_control.OnButtonUp(event)

    ###########################################################################
    # 'event' interface.
    ###########################################################################

    def _closed_fired(self):
        super(Scene, self)._closed_fired()
        self.picker = None
        self._vtk_control = None

    ###########################################################################
    # Non-public interface.
    ###########################################################################
    def _create_control(self, parent):
        """ Create the toolkit-specific control that represents the widget. """

        # Create the VTK widget.
        self._vtk_control = window = wxVTKRenderWindowInteractor(
            parent, -1, stereo=self.stereo)

        # Override these handlers.
        window.Bind(wx.EVT_CHAR, None)  # Remove the default handler.
        window.Bind(wx.EVT_CHAR, self.OnKeyDown)
        window.Bind(wx.EVT_KEY_UP, None)  # Remove the default handler.
        window.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
        window.Bind(wx.EVT_PAINT, None)  # Remove the default handler.
        window.Bind(wx.EVT_PAINT, self.OnPaint)
        window.Bind(wx.EVT_SIZE, None)  # Remove the default handler.
        window.Bind(wx.EVT_SIZE, self.OnSize)
        # Override the button down and up handlers as well to note the
        # interaction.  This is to toggle the busy status nicely.
        for evt in (wx.EVT_LEFT_DOWN, wx.EVT_RIGHT_DOWN, wx.EVT_MIDDLE_DOWN):
            window.Bind(evt, None)
            window.Bind(evt, self.OnButtonDown)
        for evt in (wx.EVT_LEFT_UP, wx.EVT_RIGHT_UP, wx.EVT_MIDDLE_UP):
            window.Bind(evt, None)
            window.Bind(evt, self.OnButtonUp)

        # Enable the widget.
        window.Enable(1)
        # Switch the default interaction style to the trackball one.
        window.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

        # Grab the renderwindow.
        renwin = self._renwin = tvtk.to_tvtk(window.GetRenderWindow())
        renwin.trait_set(point_smoothing=self.point_smoothing,
                         line_smoothing=self.line_smoothing,
                         polygon_smoothing=self.polygon_smoothing)
        # Create a renderer and add it to the renderwindow
        self._renderer = tvtk.Renderer()
        renwin.add_renderer(self._renderer)
        # Save a reference to our camera so it is not GC'd -- needed for
        # the sync_traits to work.
        self._camera = self.camera

        # Sync various traits.
        self._renderer.background = self.background
        self.sync_trait('background', self._renderer)
        self.renderer.on_trait_change(self.render, 'background')
        self._camera.parallel_projection = self.parallel_projection
        self.sync_trait('parallel_projection', self._camera)
        renwin.off_screen_rendering = self.off_screen_rendering
        self.sync_trait('off_screen_rendering', self._renwin)
        self.render_window.on_trait_change(self.render, 'off_screen_rendering')
        self.render_window.on_trait_change(self.render, 'stereo_render')
        self.render_window.on_trait_change(self.render, 'stereo_type')
        self.camera.on_trait_change(self.render, 'parallel_projection')

        def _show_parent_hack(window, parent):
            """A hack to get the VTK scene properly setup for use."""
            # Force the parent to show itself.
            parent.Show(1)
            # on some platforms, this SetSize() is necessary to cause
            # an OnPaint() when the event loop begins; else we get an
            # empty window until we force a redraw.
            window.SetSize(parent.GetSize())
            # This is necessary on slow machines in order to force the
            # wx events to be handled.
            wx.GetApp().Yield(True)
            window.Render()

        if wx.Platform == '__WXMSW__':
            _show_parent_hack(window, parent)
        else:
            if (wx.VERSION[0] == 2) and (wx.VERSION[1] < 5):
                _show_parent_hack(window, parent)
            window.Update()

        # Because of the way the VTK widget is setup, and because we
        # set the size above, the window sizing is usually completely
        # messed up when the application window is shown.  To work
        # around this a dynamic IDLE event handler is added and
        # immediately removed once it executes.  This event handler
        # simply forces a resize to occur.  The _idle_count allows us
        # to execute the idle function a few times (this seems to work
        # better).
        def _do_idle(event, window=window):
            w = wx.GetTopLevelParent(window)
            # Force a resize
            sz = w.GetSize()
            w.SetSize((sz[0] - 1, sz[1] - 1))
            w.SetSize(sz)
            window._idle_count -= 1
            if window._idle_count < 1:
                window.Bind(wx.EVT_IDLE, None)
                del window._idle_count

        window._idle_count = 2
        window.Bind(wx.EVT_IDLE, _do_idle)

        self._interactor = tvtk.to_tvtk(window._Iren)
        return window

    def _lift(self):
        """Lift the window to the top. Useful when saving screen to an
        image."""
        if self.render_window.off_screen_rendering:
            # Do nothing if off screen rendering is being used.
            return

        w = self._vtk_control
        while w and not w.IsTopLevel():
            w = w.GetParent()
        if w:
            w.Raise()
            wx.GetApp().Yield(True)
            self.render()

    def _start_event_callback(self, obj, event):
        if self._interacting:
            return
        else:
            self.busy = True

    def _end_event_callback(self, obj, event):
        if self._interacting:
            return
        else:
            self.busy = False

    def _busy_changed(self, val):
        GUI.set_busy(val)

    def _full_screen_fired(self):
        fs = self._fullscreen
        if isinstance(fs, PopupScene):
            fs.close()
            self._fullscreen = None
        elif fs is None:
            f = PopupScene(self)
            self._fullscreen = f
            f.fullscreen()

    def _disable_fullscreen(self):
        fs = self._fullscreen
        if isinstance(fs, PopupScene):
            fs.close()
            self._fullscreen = None
Ejemplo n.º 8
0
    def init(self, parent):
        """Finishes initializing the editor by creating the underlying toolkit
        widget."""

        factory = self.factory
        columns = factory.columns[:]
        if (len(columns) == 0) and (len(self.value) > 0):
            columns = [
                ObjectColumn(name=name)
                for name in self.value[0].editable_traits()
            ]
        self.columns = columns
        if factory.table_view_factory is not None:
            self.table_view = factory.table_view_factory(editor=self)
        if factory.source_model_factory is not None:
            self.source_model = factory.source_model_factory(editor=self)
        if factory.model_factory is not None:
            self.model = factory.model_factory(editor=self)

        # Create the table view and model
        self.model.setDynamicSortFilter(True)
        self.model.setSourceModel(self.source_model)
        self.table_view.setModel(self.model)

        # Create the vertical header context menu and connect to its signals
        self.header_menu = QtGui.QMenu(self.table_view)
        insertable = factory.row_factory is not None and not factory.auto_add
        if factory.editable:
            if insertable:
                action = self.header_menu.addAction('Insert new item')
                action.triggered.connect(self._on_context_insert)
            if factory.deletable:
                action = self.header_menu.addAction('Delete item')
                action.triggered.connect(self._on_context_remove)
        if factory.reorderable:
            if factory.editable and (insertable or factory.deletable):
                self.header_menu.addSeparator()
            self.header_menu_up = self.header_menu.addAction('Move item up')
            self.header_menu_up.triggered.connect(self._on_context_move_up)
            self.header_menu_down = self.header_menu.addAction(
                'Move item down')
            self.header_menu_down.triggered.connect(self._on_context_move_down)

        # Create the empty space context menu and connect its signals
        self.empty_menu = QtGui.QMenu(self.table_view)
        action = self.empty_menu.addAction('Add new item')
        action.triggered.connect(self._on_context_append)

        # When sorting is enabled, the first column is initially displayed with
        # the triangle indicating it is the sort index, even though no sorting
        # has actually been done. Sort here for UI/model consistency.
        if self.factory.sortable and not self.factory.reorderable:
            self.model.sort(0, QtCore.Qt.AscendingOrder)

        # Connect to the mode specific selection handler and select the first
        # row/column/cell. Do this before creating the edit_view to make sure
        # that it has a valid item to use when constructing its view.
        smodel = self.table_view.selectionModel()
        mode_slot = getattr(self, '_on_%s_selection' % factory.selection_mode)
        smodel.selectionChanged.connect(mode_slot)
        self.table_view.setCurrentIndex(self.model.index(0, 0))

        # Create the toolbar if necessary
        if factory.show_toolbar and len(factory.filters) > 0:
            main_view = QtGui.QWidget()
            layout = QtGui.QVBoxLayout(main_view)
            layout.setContentsMargins(0, 0, 0, 0)
            self.toolbar_ui = self.edit_traits(
                parent=parent,
                kind='subpanel',
                view=View(Group(Item('filter{View}',
                                     editor=factory._filter_editor),
                                Item('filter_summary{Results}',
                                     style='readonly'),
                                spring,
                                orientation='horizontal'),
                          resizable=True))
            self.toolbar_ui.parent = self.ui
            layout.addWidget(self.toolbar_ui.control)
            layout.addWidget(self.table_view)
        else:
            main_view = self.table_view

        # Create auxillary editor and encompassing splitter if necessary
        mode = factory.selection_mode
        if (factory.edit_view == ' ') or not mode in ('row', 'rows'):
            self.control = main_view
        else:
            if factory.orientation == 'horizontal':
                self.control = QtGui.QSplitter(QtCore.Qt.Horizontal)
            else:
                self.control = QtGui.QSplitter(QtCore.Qt.Vertical)
            self.control.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                       QtGui.QSizePolicy.Expanding)
            self.control.addWidget(main_view)
            self.control.setStretchFactor(0, 2)

            # Create the row editor below the table view
            editor = InstanceEditor(view=factory.edit_view, kind='subpanel')
            self._ui = self.edit_traits(
                parent=self.control,
                kind='subpanel',
                view=View(Item('selected_row',
                               style='custom',
                               editor=editor,
                               show_label=False,
                               resizable=True,
                               width=factory.edit_view_width,
                               height=factory.edit_view_height),
                          resizable=True,
                          handler=factory.edit_view_handler))
            self._ui.parent = self.ui
            self.control.addWidget(self._ui.control)
            self.control.setStretchFactor(1, 1)

        # Connect to the click and double click handlers
        self.table_view.clicked.connect(self._on_click)
        self.table_view.doubleClicked.connect(self._on_dclick)

        # Make sure we listen for 'items' changes as well as complete list
        # replacements
        self.context_object.on_trait_change(self.update_editor,
                                            self.extended_name + '_items',
                                            dispatch='ui')

        # Listen for changes to traits on the objects in the list
        self.context_object.on_trait_change(self.refresh_editor,
                                            self.extended_name + '.-',
                                            dispatch='ui')

        # Listen for changes on column definitions
        self.on_trait_change(self._update_columns, 'columns', dispatch='ui')
        self.on_trait_change(self._update_columns,
                             'columns_items',
                             dispatch='ui')

        # Set up the required externally synchronized traits
        is_list = (mode in ('rows', 'columns', 'cells'))
        self.sync_value(factory.click, 'click', 'to')
        self.sync_value(factory.dclick, 'dclick', 'to')
        self.sync_value(factory.columns_name, 'columns', is_list=True)
        self.sync_value(factory.selected, 'selected', is_list=is_list)
        self.sync_value(factory.selected_indices,
                        'selected_indices',
                        is_list=is_list)
        self.sync_value(factory.filter_name, 'filter', 'from')
        self.sync_value(factory.filtered_indices, 'filtered_indices', 'to')
        self.sync_value(factory.update_filter_name, 'update_filter', 'from')

        self.auto_size = self.factory.auto_size

        # Initialize the ItemDelegates for each column
        self._update_columns()
Ejemplo n.º 9
0
class Workflow(HasStrictTraits):

    workflow = List(WorkflowItem)
    selected = Instance(WorkflowItem)
    version = Str

    modified = Bool
    debug = Bool

    #     default_scale = util.ScaleEnum

    # a view for the entire workflow's list of operations
    operations_traits = View(Item('workflow',
                                  editor=VerticalNotebookEditor(
                                      view='operation_traits',
                                      page_name='.name',
                                      page_description='.friendly_id',
                                      page_icon='.icon',
                                      delete=True,
                                      page_deletable='.deletable',
                                      selected='selected',
                                      multiple_open=False),
                                  show_label=False),
                             scrollable=True)

    # a view showing the selected workflow item's current view
    selected_view_traits = View(Item(
        'selected',
        editor=InstanceEditor(view='current_view_traits'),
        style='custom',
        show_label=False),
                                Spring(),
                                Item('apply_calls',
                                     style='readonly',
                                     visible_when='debug'),
                                Item('plot_calls',
                                     style='readonly',
                                     visible_when='debug'),
                                kind='panel',
                                scrollable=True)

    # the view for the center pane
    plot_view = View(
        Item('selected',
             editor=InstanceEditor(view='current_plot_view'),
             style='custom',
             show_label=False))

    recv_thread = Instance(threading.Thread)
    send_thread = Instance(threading.Thread)
    log_thread = Instance(threading.Thread)
    remote_process_thread = Instance(threading.Thread)
    message_q = Instance(Queue, ())

    # the Pipe connection object to pass to the matplotlib canvas
    child_matplotlib_conn = Any

    # count the number of times the remote process calls apply() or plot().
    # useful for debugging
    apply_calls = Int(0)
    plot_calls = Int(0)

    def __init__(self, remote_connection, **kwargs):
        super(Workflow, self).__init__(**kwargs)

        child_workflow_conn, self.child_matplotlib_conn, log_q = remote_connection

        self.recv_thread = threading.Thread(target=self.recv_main,
                                            name="local workflow recv",
                                            args=[child_workflow_conn])
        self.recv_thread.daemon = True
        self.recv_thread.start()

        self.send_thread = threading.Thread(target=self.send_main,
                                            name="local workflow send",
                                            args=[child_workflow_conn])
        self.send_thread.daemon = True
        self.send_thread.start()

        self.log_thread = threading.Thread(target=self.log_main,
                                           name="log listener thread",
                                           args=[log_q])
        self.log_thread.daemon = True
        self.log_thread.start()

    def recv_main(self, child_conn):
        while child_conn.poll(None):
            try:
                (msg, payload) = child_conn.recv()
            except EOFError:
                return

            logging.debug("LocalWorkflow.recv_main :: {}".format(msg))

            try:
                if msg == Msg.UPDATE_WI:
                    (idx, name, new) = payload
                    wi = self.workflow[idx]

                    if not wi.trait(name).status:
                        raise RuntimeError(
                            "Tried to set a local non-status wi trait")

                    with wi.lock:
                        wi.trait_set(**{name: new})

                elif msg == Msg.UPDATE_OP:
                    (idx, name, new) = payload
                    wi = self.workflow[idx]

                    if not wi.operation.trait(name).status:
                        raise RuntimeError(
                            "Tried to set a local non-status trait")

                    with wi.lock:
                        wi.operation.trait_set(**{name: new})

                elif msg == Msg.UPDATE_VIEW:
                    (idx, view_id, name, new) = payload
                    wi = self.workflow[idx]
                    view = next((x for x in wi.views if x.id == view_id))

                    if not view.trait(name).status:
                        raise RuntimeError(
                            "Tried to set a local non-status trait")

                    with wi.lock:
                        view.trait_set(**{name: new})

                elif msg == Msg.APPLY_CALLED:
                    self.apply_calls = payload

                elif msg == Msg.PLOT_CALLED:
                    self.plot_calls = payload

                else:
                    raise RuntimeError("Bad message from remote")

            except Exception:
                log_exception()

    def send_main(self, child_conn):
        try:
            while True:
                msg = self.message_q.get()
                child_conn.send(msg)
        except Exception:
            log_exception()

    def log_main(self, log_q):
        # from http://plumberjack.blogspot.com/2010/09/using-logging-with-multiprocessing.html

        while True:
            try:
                record = log_q.get()
                if record is None:  # We send this as a sentinel to tell the listener to quit.
                    break
                logger = logging.getLogger(record.name)
                logger.handle(
                    record)  # No level or filter logic applied - just do it!
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                print('Whoops! Problem:', file=sys.stderr)
                traceback.print_exc(file=sys.stderr)

    def shutdown_remote_process(self):
        self.message_q.put((Msg.SHUTDOWN, None))

    @on_trait_change('workflow')
    def _on_new_workflow(self, obj, name, old, new):
        logging.debug("LocalWorkflow._on_new_workflow")

        self.selected = None

        # send the new workflow to the child process
        self.message_q.put((Msg.NEW_WORKFLOW, self.workflow))

    @on_trait_change('workflow_items')
    def _on_workflow_add_remove_items(self, event):
        logging.debug(
            "LocalWorkflow._on_workflow_add_remove_items :: {}".format(
                (event.index, event.removed, event.added)))

        idx = event.index
        self.modified = True

        # remove deleted items from the linked list
        if event.removed:
            assert len(event.removed) == 1
            removed = event.removed[0]
            if removed.previous_wi:
                removed.previous_wi.next_wi = removed.next_wi

            if removed.next_wi:
                removed.next_wi.previous_wi = removed.previous_wi

            self.message_q.put((Msg.REMOVE_ITEMS, idx))

            if removed == self.selected:
                self.selected = None

        # add new items to the linked list
        if event.added:
            assert len(event.added) == 1

            if idx > 0:
                # populate the new wi with metadata from the old one
                self.workflow[idx].channels = list(self.workflow[idx -
                                                                 1].channels)
                self.workflow[idx].conditions = dict(
                    self.workflow[idx - 1].conditions)
                self.workflow[idx].metadata = dict(self.workflow[idx -
                                                                 1].metadata)
                self.workflow[idx].statistics = dict(
                    self.workflow[idx - 1].statistics)

                self.workflow[idx - 1].next_wi = self.workflow[idx]
                self.workflow[idx].previous_wi = self.workflow[idx - 1]

            if idx < len(self.workflow) - 1:
                self.workflow[idx].next_wi = self.workflow[idx + 1]
                self.workflow[idx + 1].previous_wi = self.workflow[idx]

            self.message_q.put((Msg.ADD_ITEMS, (idx, event.added[0])))

    @on_trait_change('selected')
    def _on_selected_changed(self, obj, name, old, new):
        logging.debug("LocalWorkflow._on_selected_changed :: {}".format(
            (obj, name, old, new)))

        if new is None:
            idx = -1
        else:
            idx = self.workflow.index(new)

        self.message_q.put((Msg.SELECT, idx))

    @on_trait_change('workflow:operation:+')
    def _operation_changed(self, obj, name, old, new):
        logging.debug("LocalWorkflow._operation_changed :: {}".format(
            (obj, name, old, new)))

        if not obj.trait(name).transient and not obj.trait(name).status:
            wi = next((x for x in self.workflow if x.operation == obj))
            idx = self.workflow.index(wi)
            self.message_q.put((Msg.UPDATE_OP, (idx, name, new)))
            self.modified = True

    @on_trait_change('workflow:operation:changed')
    def _operation_changed_event(self, obj, _, new):
        logging.debug("LocalWorkflow._operation_changed_event:: {}".format(
            (obj, new)))

        (_, (name, new)) = new

        wi = next((x for x in self.workflow if x.operation == obj))
        idx = self.workflow.index(wi)
        self.message_q.put((Msg.UPDATE_OP, (idx, name, new)))
        self.modified = True

    @on_trait_change('workflow:views:+')
    def _view_changed(self, obj, name, old, new):
        logging.debug("LocalWorkflow._view_changed :: {}".format(
            (obj, name, old, new)))

        if not obj.trait(name).transient and not obj.trait(name).status:
            wi = next((x for x in self.workflow if obj in x.views))
            idx = self.workflow.index(wi)
            self.message_q.put((Msg.UPDATE_VIEW, (idx, obj.id, name, new)))
            self.modified = True

    @on_trait_change('workflow:views:changed')
    def _view_changed_event(self, obj, _, new):
        logging.debug("LocalWorkflow._view_changed_event:: {}".format(
            (obj, new)))

        (_, (_, name, new)) = new

        wi = next((x for x in self.workflow if obj in x.views))
        idx = self.workflow.index(wi)
        self.message_q.put((Msg.UPDATE_VIEW, (idx, obj.id, name, new)))
        self.modified = True

    @on_trait_change('workflow:current_view')
    def _on_current_view_changed(self, obj, name, old, new):
        logging.debug("LocalWorkflow._on_current_view_changed :: {}".format(
            (obj, name, old, new)))

        idx = self.workflow.index(obj)
        view = obj.current_view
        self.message_q.put((Msg.CHANGE_CURRENT_VIEW, (idx, view)))

    @on_trait_change('workflow:current_plot')
    def _on_current_plot_changed(self, obj, name, old, new):
        logging.debug("LocalWorkflow._on_current_plot_changed :: {}".format(
            (obj, name, old, new)))

        idx = self.workflow.index(obj)
        plot = obj.current_plot
        self.message_q.put((Msg.CHANGE_CURRENT_PLOT, (idx, plot)))

    @on_trait_change('workflow:operation:do_estimate')
    def _on_estimate(self, obj, name, old, new):
        logging.debug("LocalWorkflow._on_estimate :: {}".format(
            (obj, name, old, new)))

        wi = next((x for x in self.workflow if x.operation == obj))
        idx = self.workflow.index(wi)
        self.message_q.put((Msg.ESTIMATE, idx))
Ejemplo n.º 10
0
 def trait_view(self, name=None, view_elements=None):
     return View(
       VGroup(
         HSplit(
             Item( 'function_search',
                   label      = 'Search',
                   id         = 'search',
                   style      = 'custom',
                   dock       = 'horizontal',
                   show_label = False,
             ),
             VSplit(
                 Item( 'object.block_unit.codeblock_ui',
                       label      = 'Canvas',
                       id         = 'canvas',
                       editor     = InstanceEditor(
                                        view = 'canvas_view' ),
                       style      = 'custom',
                       dock       = 'horizontal',
                       show_label = False
                 ),
                 Item( 'object.block_unit.codeblock_ui',
                       label      = 'Code',
                       id         = 'code',
                       editor     = InstanceEditor( view = 'code_view' ),
                       style      = 'custom',
                       dock       = 'horizontal',
                       show_label = False
                 ),
                 # DEBUG: Comment this 'Item' out to remove debug view...
                 Item( 'object.block_unit.codeblock_ui',
                       label      = 'Debug',
                       id         = 'debug',
                       editor     = ValueEditor(),
                       style      = 'custom',
                       dock       = 'horizontal',
                       show_label = False
                 ),
                 Item( 'exec_context',
                       label      = 'Shell',
                       id         = 'shell',
                       editor     = ContextShellEditor(
                                        block_unit = self.block_unit ),
                       dock       = 'horizontal',
                       show_label = False,
                 ),
             ),
             Item( 'object.block_unit',
                   label      = 'Variables',
                   id         = 'variables',
                   editor     = InstanceEditor(
                                    view = 'variables_view' ),
                   style      = 'custom',
                   dock       ='horizontal',
                   show_label = False,
             ),
             id='panel_split',
         ),
         Item( 'status',
               style      = 'readonly',
               show_label = False,
               resizable  = False
         ),
         # fixme: This code only works on platforms for which wx.GCDC is
         # defined...
         #group_theme = '@XG1'
       ),
       title     = 'Block Canvas',
       menubar   = BlockApplicationMenuBar,
       width     = 800,
       height    = 600,
       id        = 'blockcanvas.app.block_application',
       resizable = True,
       handler   = BlockApplicationViewHandler,
     )
Ejemplo n.º 11
0
class TableFilterEditor(HasTraits):
    """ An editor that manages table filters.
    """

    #-------------------------------------------------------------------------
    #  Trait definitions:
    #-------------------------------------------------------------------------

    # TableEditor this editor is associated with
    editor = Instance(TableEditor)

    # The list of filters
    filters = List(TableFilter)

    # The list of available templates from which filters can be created
    templates = Property(List(TableFilter), depends_on='filters')

    # The currently selected filter template
    selected_template = Instance(TableFilter)

    # The currently selected filter
    selected_filter = Instance(TableFilter, allow_none=True)

    # The view to use for the current filter
    selected_filter_view = Property(depends_on='selected_filter')

    # Buttons for add/removing filters
    add_button = Button('New')
    remove_button = Button('Delete')

    # The default view for this editor
    view = View(Group(
        Group(Group(Item('add_button', enabled_when='selected_template'),
                    Item('remove_button',
                         enabled_when='len(templates) > 1 and '
                         'selected_filter is not None'),
                    orientation='horizontal',
                    show_labels=False),
              Label('Base filter for new filters:'),
              Item('selected_template', editor=EnumEditor(name='templates')),
              Item('selected_filter',
                   style='custom',
                   editor=EnumEditor(name='filters', mode='list')),
              show_labels=False),
        Item('selected_filter',
             width=0.75,
             style='custom',
             editor=InstanceEditor(view_name='selected_filter_view')),
        id='TableFilterEditorSplit',
        show_labels=False,
        layout='split',
        orientation='horizontal'),
                id='traitsui.qt4.table_editor.TableFilterEditor',
                buttons=['OK', 'Cancel'],
                kind='livemodal',
                resizable=True,
                width=800,
                height=400,
                title='Customize filters')

    #-------------------------------------------------------------------------
    #  Private methods:
    #-------------------------------------------------------------------------

    #-- Trait Property getter/setters ----------------------------------------

    @cached_property
    def _get_selected_filter_view(self):
        view = None
        if self.selected_filter:
            model = self.editor.model
            index = model.mapToSource(model.index(0, 0))
            if index.isValid():
                obj = list(self.editor.items())[index.row()]
            else:
                obj = None
            view = self.selected_filter.edit_view(obj)
        return view

    @cached_property
    def _get_templates(self):
        templates = [f for f in self.editor.factory.filters if f.template]
        templates.extend(self.filters)
        return templates

    #-- Trait Change Handlers ------------------------------------------------

    def _editor_changed(self):
        self.filters = [
            f.clone_traits() for f in self.editor.factory.filters
            if not f.template
        ]
        self.selected_template = self.templates[0]

    def _add_button_fired(self):
        """ Create a new filter based on the selected template and select it.
        """
        new_filter = self.selected_template.clone_traits()
        new_filter.template = False
        new_filter.name = new_filter._name = 'New filter'
        self.filters.append(new_filter)
        self.selected_filter = new_filter

    def _remove_button_fired(self):
        """ Delete the currently selected filter.
        """
        if self.selected_template == self.selected_filter:
            self.selected_template = self.templates[0]

        index = self.filters.index(self.selected_filter)
        del self.filters[index]
        if index < len(self.filters):
            self.selected_filter = self.filters[index]
        else:
            self.selected_filter = None

    @on_trait_change('selected_filter:name')
    def _update_filter_list(self):
        """ A hack to make the EnumEditor watching the list of filters refresh
            their text when the name of the selected filter changes.
        """
        filters = self.filters
        self.filters = []
        self.filters = filters
Ejemplo n.º 12
0
class Scene(TVTKScene, Widget):
    """A VTK interactor scene widget for pyface and PyQt.

    This widget uses a RenderWindowInteractor and therefore supports
    interaction with VTK widgets.  The widget uses TVTK.  In addition
    to the features that the base TVTKScene provides this widget
    supports:

    - saving the rendered scene to the clipboard.

    - picking data on screen.  Press 'p' or 'P' when the mouse is over
      a point that you need to pick.

    - The widget also uses a light manager to manage the lighting of
      the scene.  Press 'l' or 'L' to activate a GUI configuration
      dialog for the lights.

    - Pressing the left, right, up and down arrow let you rotate the
      camera in those directions.  When shift-arrow is pressed then
      the camera is panned.  Pressing the '+' (or '=')  and '-' keys
      let you zoom in and out.

    - full screen rendering via the full_screen button on the UI.

    """

    # The version of this class.  Used for persistence.
    __version__ = 0

    ###########################################################################
    # Traits.
    ###########################################################################

    # Turn on full-screen rendering.
    full_screen = Button('Full Screen')

    # The picker handles pick events.
    picker = Instance(picker.Picker)

    ########################################

    # Render_window's view.
    _stereo_view = Group(
        Item(name='stereo_render'),
        Item(name='stereo_type'),
        show_border=True,
        label='Stereo rendering',
    )

    # The default view of this object.
    default_view = View(Group(
        Group(
            Item(name='background'),
            Item(name='foreground'),
            Item(name='parallel_projection'),
            Item(name='disable_render'),
            Item(name='off_screen_rendering'),
            Item(name='jpeg_quality'),
            Item(name='jpeg_progressive'),
            Item(name='magnification'),
            Item(name='anti_aliasing_frames'),
            Item(name='full_screen', show_label=False),
        ),
        Group(
            Item(name='render_window',
                 style='custom',
                 visible_when='object.stereo',
                 editor=InstanceEditor(view=View(_stereo_view)),
                 show_label=False), ),
        label='Scene'),
                        Group(Item(name='light_manager',
                                   style='custom',
                                   show_label=False),
                              label='Lights'),
                        buttons=['OK', 'Cancel'])

    ########################################
    # Private traits.

    _vtk_control = Instance(_VTKRenderWindowInteractor)
    _fullscreen = Any(False)

    ###########################################################################
    # 'object' interface.
    ###########################################################################
    def __init__(self, parent=None, **traits):
        """ Initializes the object. """

        # Base class constructor.
        super(Scene, self).__init__(parent, **traits)

        # Setup the default picker.
        self.picker = picker.Picker(self)

        # The light manager needs creating.
        self.light_manager = None

        self._cursor = QtCore.Qt.ArrowCursor

    def __get_pure_state__(self):
        """Allows us to pickle the scene."""
        # The control attribute is not picklable since it is a VTK
        # object so we remove it.
        d = super(Scene, self).__get_pure_state__()
        for x in ['_vtk_control', '_fullscreen']:
            d.pop(x, None)
        return d

    ###########################################################################
    # 'Scene' interface.
    ###########################################################################
    def render(self):
        """ Force the scene to be rendered. Nothing is done if the
        `disable_render` trait is set to True."""
        if not self.disable_render:
            self._vtk_control.Render()

    def get_size(self):
        """Return size of the render window."""
        sz = self._vtk_control.size()

        return (sz.width(), sz.height())

    def set_size(self, size):
        """Set the size of the window."""
        self._vtk_control.resize(*size)

    def hide_cursor(self):
        """Hide the cursor."""
        self._cursor = self._vtk_control.cursor().shape()
        self._vtk_control.setCursor(QtCore.Qt.BlankCursor)

    def show_cursor(self):
        """Show the cursor."""
        self._vtk_control.setCursor(self._cursor)

    ###########################################################################
    # 'TVTKScene' interface.
    ###########################################################################
    def save_to_clipboard(self):
        """Saves a bitmap of the scene to the clipboard."""
        handler, name = tempfile.mkstemp()
        self.save_bmp(name)
        QtGui.QApplication.clipboard().setImage(QtGui.QImage(name))
        os.close(handler)
        os.unlink(name)

    ###########################################################################
    # 'event' interface.
    ###########################################################################
    def _closed_fired(self):
        super(Scene, self)._closed_fired()
        self.picker = None
        self._vtk_control = None
        self.control = None

    ###########################################################################
    # Non-public interface.
    ###########################################################################
    def _create_control(self, parent):
        """ Create the toolkit-specific control that represents the widget. """

        # Create the VTK widget.
        self._vtk_control = window = _VTKRenderWindowInteractor(
            self, parent, stereo=self.stereo)

        # Switch the default interaction style to the trackball one.
        window.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

        # Grab the renderwindow.
        renwin = self._renwin = tvtk.to_tvtk(window.GetRenderWindow())
        renwin.set(point_smoothing=self.point_smoothing,
                   line_smoothing=self.line_smoothing,
                   polygon_smoothing=self.polygon_smoothing)
        # Create a renderer and add it to the renderwindow
        self._renderer = tvtk.Renderer()
        renwin.add_renderer(self._renderer)
        # Save a reference to our camera so it is not GC'd -- needed for
        # the sync_traits to work.
        self._camera = self.camera

        # Sync various traits.
        self._renderer.background = self.background
        self.sync_trait('background', self._renderer)
        self.renderer.on_trait_change(self.render, 'background')
        renwin.off_screen_rendering = self.off_screen_rendering
        self._camera.parallel_projection = self.parallel_projection
        self.sync_trait('parallel_projection', self._camera)
        self.sync_trait('off_screen_rendering', self._renwin)
        self.render_window.on_trait_change(self.render, 'off_screen_rendering')
        self.render_window.on_trait_change(self.render, 'stereo_render')
        self.render_window.on_trait_change(self.render, 'stereo_type')
        self.camera.on_trait_change(self.render, 'parallel_projection')

        self._interactor = tvtk.to_tvtk(window._Iren)

        return window

    def _lift(self):
        """Lift the window to the top. Useful when saving screen to an
        image."""
        if self.render_window.off_screen_rendering:
            # Do nothing if off screen rendering is being used.
            return

        self._vtk_control.window().raise_()
        QtCore.QCoreApplication.processEvents()

    def _full_screen_fired(self):
        fs = self._fullscreen
        if fs:
            self._vtk_control.window().showNormal()
            self._fullscreen = False
        else:
            self._vtk_control.window().showFullScreen()
            self._fullscreen = True

    def _disable_fullscreen(self):
        fs = self._fullscreen
        if fs:
            self._vtk_control.window().showNormal()
            self._fullscreen = False

    def _busy_changed(self, val):
        GUI.set_busy(val)
Ejemplo n.º 13
0
class ExperimentDialogHandler(Controller):
    
    
    # bits for model initialization
    import_op = Instance('cytoflowgui.op_plugins.import_op.ImportPluginOp')
        
    # events
    add_tubes = Event
    remove_tubes = Event
    add_variable = Event
    import_csv = Event
    
    # traits to communicate with the TabularEditor
    selected_tubes = List
    
    default_view = View(
            HGroup(
                VGroup(
                    Label("Variables"),
                    Item('tube_traits',
                         editor = VerticalListEditor(editor = InstanceEditor(),
                                                     style = 'custom',
                                                     mutable = False,
                                                     deletable = True),
                         show_label = False),
                    HGroup(
                        Item('handler.add_variable',
                             editor = ButtonEditor(label = "Add a variable"),
                             show_label = False))),
                VGroup(
                    Label("Tubes"),
                    Item(name = 'tubes', 
                         id = 'table', 
                         editor = TableEditor(editable = True,
                                              sortable = True,
                                              auto_size = True,
                                              configurable = False,
                                              selection_mode = 'rows',
                                              selected = 'handler.selected_tubes',
                                              columns = [ObjectColumn(name = 'index',
                                                                      read_only_cell_color = 'light grey',
                                                                      editable = False)]),
                         enabled_when = "object.tubes",
                         show_label = False),
                    HGroup(
                        Item('handler.add_tubes',
                             editor = ButtonEditor(label = "Add tubes..."),
                             show_label = False),
                        Item('handler.remove_tubes',
                             editor = ButtonEditor(label = "Remove tubes"),
                             show_label = False,
                             enabled_when = 'object.tubes'),
                        Item('handler.import_csv',
                             editor = ButtonEditor(label = "Import from CSV..."),
                             show_label = False)))),
            title     = 'Experiment Setup',
            buttons = OKCancelButtons,
            resizable = True,
            width = 0.3,
            height = 0.3
        )

    # keep a ref to the table editor so we can add columns dynamically
    table_editor = Instance(TableEditorQt)
    
    updating = Bool(False)
    
    def init(self, info):
                       
        # save a reference to the table editor
        self.table_editor = info.ui.get_editors('tubes')[0]
        
        # init the model
        self.model.init(self.import_op)
                
        return True
    
    def close(self, info, is_ok):
        """ Handles the user attempting to close a dialog-based user interface.

        This method is called when the user attempts to close a window, by
        clicking an **OK** or **Cancel** button, or clicking a Close control
        on the window). It is called before the window is actually destroyed.
        Override this method to perform any checks before closing a window.

        While Traits UI handles "OK" and "Cancel" events automatically, you
        can use the value of the *is_ok* parameter to implement additional
        behavior.

        Parameters
        ----------
        info : UIInfo object
            The UIInfo object associated with the view
        is_ok : Boolean
            Indicates whether the user confirmed the changes (such as by
            clicking **OK**.)

        Returns
        -------
        allow_close : bool
            A Boolean, indicating whether the window should be allowed to close.
        """
        if is_ok:
            if not self.model.valid:
                error(None, 
                      "Each tube must have a unique set of experimental conditions",
                      "Invalid experiment!")
                return False

        if not is_ok:
            # we don't need to "undo" anything, we're throwing this model away
            info.ui.history = None

        return True
    
    
    def closed(self, info, is_ok):
        for trait in self.model.tube_traits:
            if trait.type != 'metadata':
                for tube in self.model.tubes:
                    tube.on_trait_change(self._try_multiedit, 
                                         trait.name, 
                                         remove = True)
        if is_ok:
            self.model.update_import_op(self.import_op)
        
            
    @on_trait_change('add_variable')
    def _on_add_variable(self):
        self.model.tube_traits.append(TubeTrait(model = self.model))
        
            
    @on_trait_change('import_csv')
    def _on_import(self):
        """
        Import format: CSV, first column is filename, path relative to CSV.
        others are conditions, type is autodetected.  first row is header
        with names.
        """
        file_dialog = FileDialog()
        file_dialog.wildcard = "CSV files (*.csv)|*.csv|"
        file_dialog.action = 'open'
        file_dialog.open()
        
        if file_dialog.return_code != PyfaceOK:
            return
        
        csv = pandas.read_csv(file_dialog.path)
        csv_folder = Path(file_dialog.path).parent
        
        if self.model.tubes or self.model.tube_traits:
            if confirm(parent = None,
                       message = "This will clear the current conditions and tubes! "
                                 "Are you sure you want to continue?",
                       title = "Clear tubes and conditions?") != YES:
                return
        
        
        for col in csv.columns[1:]:
            self.model.tube_traits.append(TubeTrait(model = self.model,
                                                    name = util.sanitize_identifier(col),
                                                    type = 'category'))
            
            
        for _, row in csv.iterrows():
            filename = csv_folder / row[0]
            
            try:
                metadata, _ = parse_tube(str(filename), metadata_only = True)
            except Exception as e:
                warning(None, "Had trouble loading file {}: {}".format(filename, str(e)))
                continue

            metadata['CF_File'] = Path(filename).stem
            new_tube = Tube(file = str(filename), parent = self.model, metadata = sanitize_metadata(metadata))
            self.model.tubes.append(new_tube)

            for col in csv.columns[1:]:
                new_tube.trait_set(**{util.sanitize_identifier(col) : row[col]})
            
        
        
    @on_trait_change('add_tubes')
    def _on_add_tubes(self):
        """
        Handle "Add tubes..." button.  Add tubes to the experiment.
        """
        
        file_dialog = FileDialog()
        file_dialog.wildcard = "Flow cytometry files (*.fcs *.lmd)|*.fcs *.lmd|"
        file_dialog.action = 'open files'
        file_dialog.open()
        
        if file_dialog.return_code != PyfaceOK:
            return
        
        for path in file_dialog.paths:
            try:
                metadata, _ = parse_tube(path, metadata_only = True)
            except Exception as e:
                raise RuntimeError("FCS reader threw an error on tube {0}: {1}"\
                                   .format(path, e.value))
                
            # if we're the first tube loaded, create a dummy experiment
            # and setup default metadata columns
            if not self.model.dummy_experiment:
                self.model.dummy_experiment = \
                    ImportOp(tubes = [CytoflowTube(file = path)]).apply(metadata_only = True)
                                                       
            # check the next tube against the dummy experiment
            try:
                check_tube(path, self.model.dummy_experiment)
            except util.CytoflowError as e:
                error(None, e.__str__(), "Error importing tube")
                return
            
            metadata['CF_File'] = Path(path).stem    
            tube = Tube(file = path, parent = self.model, metadata = sanitize_metadata(metadata))
            
            self.model.tubes.append(tube)
            
            for trait in self.model.tube_traits:
                if trait.type != 'metadata' and trait.name:
                    tube.on_trait_change(self._try_multiedit, trait.name)
         
            
    @on_trait_change('remove_tubes')
    def _on_remove_tubes(self):
        conf = confirm(None,
                       "Are you sure you want to remove the selected tube(s)?",
                       "Remove tubes?")
        if conf == YES:
            for tube in self.selected_tubes:
                self.model.tubes.remove(tube)
                
        if not self.model.tubes:
            self.model.dummy_experiment = None

                
    @on_trait_change('model:tube_traits_items', post_init = True)
    def _tube_traits_changed(self, event):
        for trait in event.added:
            if not trait.name:
                continue
            
            if self.table_editor:
                self.table_editor.columns.append(ExperimentColumn(name = trait.name,
                                                                  editable = (trait.type != 'metadata')))                 
            for tube in self.model.tubes:   
                if trait.type != 'metadata' and trait.name:
                    tube.on_trait_change(self._try_multiedit, trait.name)

        for trait in event.removed:
            if not trait.name:
                continue
            
            table_column = next((x for x in self.table_editor.columns if x.name == trait.name))
            self.table_editor.columns.remove(table_column)
            
            for tube in self.model.tubes:
                if trait.type != 'metadata' and trait.name:
                    tube.on_trait_change(self._try_multiedit, trait.name, remove = True)
                    
                    
    @on_trait_change('model:tube_traits:name')
    def _tube_trait_name_changed(self, trait, _, old_name, new_name):
        if old_name:
            old_table_column = next((x for x in self.table_editor.columns if x.name == old_name))
            column_idx = self.table_editor.columns.index(old_table_column)
        else:
            column_idx = len(self.table_editor.columns)
                
        if new_name:
            self.table_editor.columns.insert(column_idx,
                                             ExperimentColumn(name = new_name,
                                                              editable = (trait.type != 'metadata')))
            
        if old_name:          
            self.table_editor.columns.remove(old_table_column)
               
        for tube in self.model.tubes:   
            if trait.type != 'metadata':
                if old_name:
                    tube.on_trait_change(self._try_multiedit, old_name, remove = True)
                    
                if new_name:
                    tube.on_trait_change(self._try_multiedit, new_name)
                
            if old_name:
                tube.remove_trait(old_name)
                
            
        self.model.counter.clear()
        for tube in self.model.tubes:
            tube_hash = tube.conditions_hash()
            if tube_hash in self.model.counter:
                self.model.counter[tube_hash] += 1
            else:
                self.model.counter[tube_hash] = 1
                
    @on_trait_change('model:tube_traits:type')
    def _tube_trait_type_changed(self, trait, _, old_type, new_type):
        if not trait.name:
            return
        
        table_column = next((x for x in self.table_editor.columns if x.name == trait.name))
        
        table_column.editable = (new_type != 'metadata')  
        
        for tube in self.model.tubes:
            if trait.name:
                if old_type != 'metadata':
                    tube.on_trait_change(self._try_multiedit, trait.name, remove = True)
                    
                if new_type != 'metadata':
                    tube.on_trait_change(self._try_multiedit, trait.name)
                  
                    
            
    def _try_multiedit(self, obj, name, old, new):
        """
        See if there are multiple elements selected when a tube's trait changes
        
        and if so, edit the same trait for all the selected tubes.
        """
        
        if self.updating:
            return
        
        self.updating = True

        for tube in self.selected_tubes:
            if tube != obj:
                old_hash = tube.conditions_hash()
                self.model.counter[old_hash] -= 1
                if self.model.counter[old_hash] == 0:
                    del self.model.counter[old_hash]            
    
                # update the underlying traits without notifying the editor
                # we do this all here for performance reasons
                tube.trait_setq(**{name: new})
                tube.conditions[name] = new
                
                new_hash = tube.conditions_hash()
                if new_hash not in self.model.counter:
                    self.model.counter[new_hash] = 1
                else:
                    self.model.counter[new_hash] += 1
                

        # now refresh the editor all at once
        self.table_editor.refresh_editor()
        self.updating = False
Ejemplo n.º 14
0
    def traits_view(self):
        pos_grp = VGroup(
            UItem('move_enabled_button',
                  editor=ButtonEditor(label_value='move_enabled_label')),
            VGroup(
                HGroup(
                    Item('position'),
                    UItem('stage_manager.stage_map_name',
                          editor=EnumEditor(
                              name='stage_manager.stage_map_names')),
                    UItem('stage_stop_button')),
                # Item('x', editor=RangeEditor(low=-25.0, high=25.0)),
                # Item('y', editor=RangeEditor(low=-25.0, high=25.0)),
                # Item('z', editor=RangeEditor(low=-25.0, high=25.0)),
                Item('x',
                     editor=RangeEditor(low_name='stage_manager.xmin',
                                        high_name='stage_manager.xmax')),
                Item('y',
                     editor=RangeEditor(low_name='stage_manager.ymin',
                                        high_name='stage_manager.ymax')),
                Item('z',
                     editor=RangeEditor(low_name='stage_manager.zmin',
                                        high_name='stage_manager.zmax')),
                enabled_when='_move_enabled'),
            label='Positioning')

        calibration_grp = VGroup(
            UItem('tray_calibration.style',
                  enabled_when='not tray_calibration.isCalibrating()'),
            UItem('tray_calibration.calibrate',
                  editor=ButtonEditor(
                      label_value='tray_calibration.calibration_step')),
            HGroup(
                Item('tray_calibration.x',
                     format_str='%0.3f',
                     style='readonly'),
                Item('tray_calibration.y',
                     format_str='%0.3f',
                     style='readonly')),
            Item('tray_calibration.rotation',
                 format_str='%0.3f',
                 style='readonly'),
            Item('tray_calibration.scale',
                 format_str='%0.4f',
                 style='readonly'),
            Item('tray_calibration.error',
                 format_str='%0.2f',
                 style='readonly'),
            UItem('tray_calibration.calibrator',
                  style='custom',
                  editor=InstanceEditor()),
            CustomLabel('tray_calibration.calibration_help',
                        color='green',
                        height=75,
                        width=300),
            label='Tray Calibration')

        tgrp = Group(pos_grp, calibration_grp, layout='tabbed')

        egrp = HGroup(
            UItem('enabled', editor=LEDEditor(colors=['red', 'green'])),
            UItem('enable', editor=ButtonEditor(label_value='enable_label')),
            UItem('fire_laser_button',
                  editor=ButtonEditor(label_value='fire_label'),
                  enabled_when='enabled'), Item('output_power', label='Power'),
            UItem('units'), spring,
            icon_button_editor('snapshot_button', 'camera'),
            icon_button_editor('test_connection_button',
                               'connect',
                               tooltip='Test Connection'))
        v = View(VGroup(egrp, tgrp))
        return v
Ejemplo n.º 15
0
class ViewHandlerMixin(HasTraits):
    """
    Useful bits for view handlers. 
    """
    
    # the view for the current plot
    current_plot_view = \
        View(
            HGroup(
                Item('plot_names_by',
                     editor = TextEditor(),
                     style = "readonly",
                     show_label = False),
                Item('current_plot',
                     editor = TabListEditor(name = 'plot_names'),
                     style = 'custom',
                     show_label = False)))
        
    plot_params_traits = View(Item('plot_params',
                                   editor = InstanceEditor(),
                                   style = 'custom',
                                   show_label = False))
    
    context = Instance(WorkflowItem)
    
    conditions_names = Property(depends_on = "context.conditions")
    previous_conditions_names = Property(depends_on = "context.previous_wi.conditions")
    statistics_names = Property(depends_on = "context.statistics")
    numeric_statistics_names = Property(depends_on = "context.statistics")
    
    # MAGIC: gets value for property "conditions_names"
    def _get_conditions_names(self):
        if self.context and self.context.conditions:
            return sorted(list(self.context.conditions.keys()))
        else:
            return []
    
    # MAGIC: gets value for property "previous_conditions_names"
    def _get_previous_conditions_names(self):
        if self.context and self.context.previous_wi and self.context.previous_wi.conditions:
            return sorted(list(self.context.previous_wi.conditions.keys()))
        else:
            return []
        
    # MAGIC: gets value for property "statistics_names"
    def _get_statistics_names(self):
        if self.context and self.context.statistics:
            return sorted(list(self.context.statistics.keys()))
        else:
            return []

    # MAGIC: gets value for property "numeric_statistics_names"
    def _get_numeric_statistics_names(self):
        if self.context and self.context.statistics:
            return sorted([x for x in list(self.context.statistics.keys())
                                 if util.is_numeric(self.context.statistics[x])])
        else:
            return []

    @on_trait_change('context.view_error_trait', 
                     dispatch = 'ui', 
                     post_init = True)
    def _view_trait_error(self):
        
        # check if we're getting called on the local or remote process
        if self.info is None or self.info.ui is None:
            return
        
        for ed in self.info.ui._editors:  
                          
            if ed.name == self.context.view_error_trait:
                err_state = True
            else:
                err_state = False

            if not ed.label_control:
                continue
            
            item = ed.label_control
            
            if not err_state and not hasattr(item, '_ok_color'):
                continue
            
            pal = QtGui.QPalette(item.palette())  # @UndefinedVariable
            
            if err_state:
                setattr(item, 
                        '_ok_color', 
                        QtGui.QColor(pal.color(item.backgroundRole())))  # @UndefinedVariable
                pal.setColor(item.backgroundRole(), QtGui.QColor(255, 145, 145))  # @UndefinedVariable
                item.setAutoFillBackground(True)
                item.setPalette(pal)
            else:
                pal.setColor(item.backgroundRole(), item._ok_color)
                delattr(item, '_ok_color')
                item.setAutoFillBackground(False)
                item.setPalette(pal)
Ejemplo n.º 16
0
class bare_sphere(ABCsphere):
    """Full mie solution to plain sphere"""

    bare_sphere_group = Group(
        Include('basic_group'),
        HGroup(Item('r_core', label='Core Radius'), ),
    )

    traits_view = View(VGroup(
        Include('bare_sphere_group'),
        HGroup(
            Item('MediumMaterial',
                 editor=InstanceEditor(),
                 style='simple',
                 show_label=False),
            Item('CoreMaterial',
                 editor=InstanceEditor(),
                 style='simple',
                 show_label=False),
        )),
                       buttons=['OK', 'Cancel', 'Undo', 'Help'])

    def update_cross(self):
        for i in range(
                self.ecore.shape[0]
        ):  #XX! Can't remove; bessel functions can't generate with full arrays
            ext_term = 0.0
            scatt_term = 0.0
            ext_old = 50
            asum = 0.0
            bsum = 0.0
            n = 0
            while self.cutoff == False and n <= self.bessmax:  #DYNAMIC CONVERGENCE, WITH SAFETY NET
                n = n + 1  #  SUM FROM 1 TO INFINITY!!!
                k = self.k_medium[i]
                x = k * self.r_core
                m1 = self.ncore[i] / self.nmedium[
                    i]  #m1 is really just m in book
                Px, dPx, Xx, dXx = self.bessy(n, x)[0:4]  #Riccati bessel of X
                Pmx, dPmx, Xmx = self.bessy(n,
                                            m1 * x)[0:3]  #Ricatti bessel of MX

                f1 = m1 * Pmx * dPx
                f2 = Px * dPmx
                f3 = m1 * Pmx * dXx
                f4 = Xx * dPmx
                a = (f1 - f2) / (f3 - f4)

                f1 = f1 / m1
                f2 = m1 * f2
                f3 = f3 / m1
                f4 = m1 * f4  #Relation between at and be is simply adjusting these by m or 1/m
                b = (f1 - f2) / (f3 - f4)

                asum = asum + a
                bsum = bsum + b
                AB = a + b

                # Why only real components in ext term?
                ext_term = ext_term + ((2.0 * n + 1.0) * AB.real)  #UNITLESS
                scatt_term = scatt_term + ((2.0 * n + 1.0)) * ((abs(a))**2 +
                                                               (abs(b))**2)

                #Cutoff the loop, then reset the switch after#
                self.cutoff_check(
                    ext_term, ext_old
                )  #THIS ONLY CHECKS THE SCATTERING TERM AN FOR CONVERGENCE, MAY NOT THE FIRST TO CONVERGE IN ALL INSTANCES
                ext_old = ext_term
            self.cutoff_reset()

            self.Cext[i] = ((2.0 * math.pi) / (k**2)) * ext_term
            self.Cscatt[i] = ((2.0 * math.pi) /
                              (k**2)) * scatt_term  #UNITS DEFINED BY 1/K**2
            self.Cabs[i] = self.Cext[i] - self.Cscatt[i]

        self.sview.update_data()

    def update_cross_new(self):
        #           for i in range(self.ecore.shape[0]):   #XX! Can't remove; bessel functions can't generate with full arrays
        ext_term = 0.0
        scatt_term = 0.0
        ext_old = 50
        asum = 0.0
        bsum = 0.0
        n = 0
        while self.cutoff == False and n <= self.bessmax:  #DYNAMIC CONVERGENCE, WITH SAFETY NET
            n += 1  #  SUM FROM 1 TO INFINITY, NOT 0!!!
            k = self.k_medium
            x = k * self.r_core
            m1 = self.ncore / self.nmedium  #m1 is really just m in book

            Px, dPx, Xx, dXx = self.bessy(n, x)[0:4]  #Riccati bessel of X
            Pmx, dPmx, Xmx = self.bessy(n, m1 * x)[0:3]  #Ricatti bessel of MX

            f1 = m1 * Pmx * dPx
            f2 = Px * dPmx
            f3 = m1 * Pmx * dXx
            f4 = Xx * dPmx
            a = (f1 - f2) / (f3 - f4)

            f1 = f1 / m1
            f2 = m1 * f2
            f3 = f3 / m1
            f4 = m1 * f4  #Relation between at and be is simply adjusting these by m or 1/m
            b = (f1 - f2) / (f3 - f4)

            asum = asum + a
            bsum = bsum + b
            AB = a + b

            # Why only real components in ext term?
            ext_term += ((2.0 * n + 1.0) * AB.real)  #UNITLESS
            scatt_term += ((2.0 * n + 1.0)) * ((abs(a))**2 + (abs(b))**2)

            #Cutoff the loop, then reset the switch after#
            self.cutoff_check(
                ext_term, ext_old
            )  #THIS ONLY CHECKS THE SCATTERING TERM AN FOR CONVERGENCE, MAY NOT THE FIRST TO CONVERGE IN ALL INSTANCES
            ext_old = ext_term

        self.cutoff_reset()

        self.Cext = ((2.0 * math.pi) / (k**2)) * ext_term
        self.Cscatt = ((2.0 * math.pi) /
                       (k**2)) * scatt_term  #UNITS DEFINED BY 1/K**2
        self.Cabs = self.Cext[i] - self.Cscatt[i]
        self.sview.update_data()
Ejemplo n.º 17
0
class UpdateView(HasTraits):
    piksi_hw_rev = String('piksi_multi')
    is_v2 = Bool(False)

    piksi_stm_vers = String('Waiting for Piksi to send settings...',
                            width=COLUMN_WIDTH)
    newest_stm_vers = String('Downloading Latest Firmware info...')
    piksi_nap_vers = String('Waiting for Piksi to send settings...')
    newest_nap_vers = String('Downloading Latest Firmware info...')
    local_console_vers = String('v' + CONSOLE_VERSION)
    newest_console_vers = String('Downloading Latest Console info...')
    download_directory_label = String('Firmware Download Directory:')

    update_stm_firmware = Button(label='Update Firmware')

    updating = Bool(False)
    update_stm_en = Bool(False)

    download_firmware = Button(label='Download Latest Firmware')
    download_directory = String()
    choose_dir = Button(label='...', padding=-1)
    download_stm = Button(label='Download', height=HT)
    downloading = Bool(False)
    download_fw_en = Bool(False)

    stm_fw = Instance(FirmwareFileDialog)

    stream = Instance(OutputStream)

    view = View(
        VGroup(Item('piksi_hw_rev',
                    label='Hardware Revision',
                    editor_args={'enabled': False},
                    resizable=True),
               HGroup(
                   VGroup(Item('piksi_stm_vers',
                               label='Current',
                               resizable=True,
                               editor_args={'enabled': False}),
                          Item('newest_stm_vers',
                               label='Latest',
                               resizable=True,
                               editor_args={
                                   'enabled': False,
                                   'readonly_allow_selection': True
                               }),
                          Item('stm_fw',
                               style='custom',
                               show_label=True,
                               label="Local File"),
                          Item('update_stm_firmware',
                               show_label=False,
                               enabled_when='update_stm_en'),
                          show_border=True,
                          label="Firmware Version"),
                   VGroup(Item('local_console_vers',
                               label='Current',
                               resizable=True,
                               editor_args={'enabled': False}),
                          Item('newest_console_vers',
                               label='Latest',
                               editor_args={'enabled': False}),
                          label="Swift Console Version",
                          show_border=True),
               ),
               HGroup(
                   VGroup(HGroup(
                       Item('download_directory',
                            label="Directory",
                            resizable=True),
                       UItem('choose_dir', width=-0.1),
                   ),
                          HGroup(
                              Spring(width=50, springy=False),
                              Item('download_firmware',
                                   enabled_when='download_fw_en',
                                   show_label=False,
                                   resizable=True,
                                   springy=True)),
                          label="Firmware Download",
                          show_border=True),
                   VGroup(Item(
                       'stream',
                       style='custom',
                       editor=InstanceEditor(),
                       show_label=False,
                   ),
                          show_border=True,
                          label="Firmware Upgrade Status"),
               ),
               show_border=True), )

    def __init__(self,
                 link,
                 download_dir=None,
                 prompt=True,
                 connection_info={'mode': 'unknown'}):
        """
        Traits tab with UI for updating Piksi firmware.

        Parameters
        ----------
        link : sbp.client.handler.Handler
          Link for SBP transfer to/from Piksi.
        prompt : bool
          Prompt user to update console/firmware if out of date.
        """
        self.link = link
        self.connection_info = connection_info
        self.settings = {}
        self.prompt = prompt
        self.python_console_cmds = {'update': self}
        self.download_directory = download_dir
        try:
            self.update_dl = UpdateDownloader(root_dir=self.download_directory)
        except RuntimeError:
            self.update_dl = None
        self.stm_fw = FirmwareFileDialog(self.download_directory)
        self.stm_fw.on_trait_change(self._manage_enables, 'status')
        self.stream = OutputStream()
        self.stream.max_len = 1000
        self.last_call_fw_version = None
        self.link.add_callback(self.log_cb, SBP_MSG_LOG)

    def _choose_dir_fired(self):
        dialog = DirectoryDialog(label='Choose Download location',
                                 action='open',
                                 default_directory=self.download_directory)
        dialog.open()
        if dialog.return_code == OK:
            self.download_directory = dialog.path
        else:
            self._write('Error while selecting firmware download location')

    def _manage_enables(self):
        """ Manages whether traits widgets are enabled in the UI or not. """
        if self.updating or self.downloading:
            self.update_stm_en = False
            self.download_fw_en = False
        else:
            if getattr(self.stm_fw, 'blob', None) is not None:
                self.update_stm_en = True
            else:
                self.update_stm_en = False
            if self.download_directory != '':
                self.download_fw_en = True

    def _download_directory_changed(self):
        if getattr(self, 'update_dl', None):
            self.update_dl.set_root_path(self.download_directory)
        self._manage_enables()

    def _updating_changed(self):
        """ Handles self.updating trait being changed. """
        self._manage_enables()

    def _downloading_changed(self):
        """ Handles self.downloading trait being changed. """
        self._manage_enables()

    def _clear_stream(self):
        self.stream.reset()

    def _write(self, text):
        """
        Stream style write function. Allows flashing debugging messages to be
        routed to embedded text console.

        Parameters
        ----------
        text : string
          Text to be written to screen.
        """
        self.stream.write(text)
        self.stream.write('\n')
        self.stream.flush()

    def _update_stm_firmware_fired(self):
        """
        Handle update_stm_firmware button. Starts thread so as not to block the GUI
        thread.
        """
        ins_settings = self.settings.get('ins', None)
        ins_output_mode = None
        if ins_settings is not None:
            ins_output_mode = ins_settings.get('output_mode').value

        if (ins_output_mode
                is not None) and not (ins_output_mode.startswith('Disabled') or
                                      ins_output_mode.startswith('disabled')):
            ins_disable_prompt = \
                prompt.CallbackPrompt(
                    title="Unsupported Update Request",
                    actions=[prompt.close_button],
                )
            ins_disable_prompt.text = \
                "\n\n" + \
                "Updating firmware is not supported when INS is active.\n\n" + \
                "Please change the 'output mode' INS setting to 'Disabled'\n" + \
                "before updating firmware.\n\n"
            ins_disable_prompt.run(block=False)
            return

        if self.connection_info['mode'] != 'TCP/IP':
            self._write(
                "\n"
                "-----------------------------------------------\n"
                "USB Flashdrive Upgrade Procedure\n"
                "-----------------------------------------------\n"
                "\n"
                "1.\tInsert the USB flash drive provided with your Piksi Multi into your computer.\n"
                "  \tSelect the flash drive root directory as the firmware download destination using the directory chooser above.\n"
                "  \tPress the \"Download Latest Firmware\" button. This will download the latest Piksi Multi firmware file onto\n"
                "  \tthe USB flashdrive.\n"
                "2.\tEject the drive from your computer and plug it into the USB Host port of the Piksi Multi evaluation board.\n"
                "3.\tReset your Piksi Multi and it will upgrade to the version on the USB flash drive.\n"
                "  \tThis should take less than 5 minutes.\n"
                "4.\tWhen the upgrade completes you will be prompted to remove the USB flash drive and reset your Piksi Multi.\n"
                "5.\tVerify that the firmware version has upgraded via inspection of the Current Firmware Version box\n"
                "  \ton the Update Tab of the Swift Console.\n")

            confirm_prompt = prompt.CallbackPrompt(
                title="Update device over serial connection?",
                actions=[
                    prompt.close_button, prompt.continue_via_serial_button
                ],
                callback=self._update_stm_firmware_fn)
            confirm_prompt.text = "\n" \
                                  + "    Upgrading your device via UART / RS232 may take up to 30 minutes.     \n" \
                                  + "                                                                          \n" \
                                  + "    If the device you are upgrading has an accessible USB host port, it   \n" \
                                    "    is recommended to instead  follow the \'USB Flashdrive Upgrade        \n" \
                                    "    Procedure\' that now appears in the Firmware upgrade status box.      \n" \
                                  + "\n" \
                                  + "    Are you sure you want to continue upgrading over serial?"
            confirm_prompt.run(block=False)
        else:
            self._update_stm_firmware_fn()

    def _replace_with_version_2(self):
        self.downloading = True
        self._write('Downloading Multi firmware v2.0.0')
        filepath = self.update_dl._download_file_from_url(V2_LINK)
        self._write('Saved file to %s' % filepath)
        self.stm_fw.load_bin(filepath)
        self.downloading = False

    def _update_stm_firmware_fn(self):
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        current_fw_version = parse_version(self.piksi_stm_vers)
        re_result = re.search('[a-zA-Z0-9]*-(v[0-9]*\.[0-9]*\.[0-9])',
                              self.stm_fw.status)
        intended_version = parse_version(re_result.group(1))
        # If the current firmware is not yet beyond 2.0.0, and we are loading beyond 2.0.0
        # warn the user that this upgrade is not possible
        if (current_fw_version < pkparse_version("v2.0.0")
                and intended_version > pkparse_version("v2.0.0")):
            confirm_prompt = prompt.CallbackPrompt(
                title="Update to v2.0.0",
                actions=[prompt.close_button, prompt.ok_button],
                callback=self._replace_with_version_2)
            confirm_prompt.text = "\n" \
                                  + "    Upgrading to firmware v2.1.0 or later requires that the device be     \n" \
                                  + "    running firmware v2.0.0 or later. Please upgrade to firmware          \n" \
                                  + "    version 2.0.0.                                                        \n" \
                                  + "                                                                          \n" \
                                  + "    Would you like to download firmware version v2.0.0 now?               \n" \
                                  + "                                                                          \n"
            confirm_prompt.run(block=False)
            return
        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("STM", ))
        self._firmware_update_thread.start()

    def _download_firmware(self):
        """ Download latest firmware from swiftnav.com. """
        self._write('')

        # Check that we received the index file from the website.
        if self.update_dl is None or self.update_dl.index is None:
            self._write("Error: Can't download firmware files")
            return

        self.downloading = True
        status = 'Downloading Latest Firmware...'
        self.stm_fw.clear(status)
        self._write(status)

        # Get firmware files from Swift Nav's website, save to disk, and load.
        if 'fw' in self.update_dl.index[self.piksi_hw_rev]:
            try:
                self._write('Downloading Latest Multi firmware')
                filepath = self.update_dl.download_multi_firmware(
                    self.piksi_hw_rev)
                self._write('Saved file to %s' % filepath)
                self.stm_fw.load_bin(filepath)
            except AttributeError:
                self._write(
                    "Error downloading firmware: index file not downloaded yet"
                )
            except RuntimeError as e:
                self._write(
                    "RunTimeError: unable to download firmware to path {0}: {1}"
                    .format(self.download_directory, e))
            except IOError as e:
                if e.errno == errno.EACCES or e.errno == errno.EPERM:
                    self._write("IOError: unable to write to path %s. "
                                "Verify that the path is writable." %
                                self.download_directory)
                else:
                    raise (e)
            except KeyError:
                self._write(
                    "Error downloading firmware: URL not present in index")
            except URLError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error: Failed to download latest NAP firmware from Swift Navigation's website"
                )
            self.downloading = False
            return

    def _download_firmware_fired(self):
        """
        Handle download_firmware button. Starts thread so as not to block the GUI
        thread.
        """
        try:
            if self._download_firmware_thread.is_alive():
                return
        except AttributeError:
            pass

        self._download_firmware_thread = Thread(target=self._download_firmware)
        self._download_firmware_thread.start()

    def compare_versions(self):
        """
        To be called after latest Piksi firmware info has been received from
        device, to decide if current firmware on Piksi is out of date. Also informs
        user if the firmware was successfully upgraded. Starts a thread so as not
        to block GUI thread.
        """
        try:
            if self._compare_versions_thread.is_alive():
                return
        except AttributeError:
            pass

        self._compare_versions_thread = Thread(target=self._compare_versions)
        self._compare_versions_thread.start()

    def _compare_versions(self):
        """
        Compares version info between received firmware version / current console
        and firmware / console info from website to decide if current firmware or
        console is out of date. Prompt user to update if so. Informs user if
        firmware successfully upgraded.
        """
        # Check that settings received from Piksi contain FW versions.
        try:
            self.piksi_hw_rev = HW_REV_LOOKUP[self.settings['system_info']
                                              ['hw_revision'].value]
            self.piksi_stm_vers = self.settings['system_info'][
                'firmware_version'].value
        except KeyError:
            self._write(
                "\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n"
            )
            return

        self.is_v2 = self.piksi_hw_rev.startswith('piksi_v2')

        self._get_latest_version_info()

        # Check that we received the index file from the website.
        if self.update_dl is None:
            self._write(
                "Error: No website index to use to compare versions with local firmware"
            )
            return
        # Get local stm version
        local_stm_version = None
        local_serial_number = None
        try:
            local_stm_version = self.settings['system_info'][
                'firmware_version'].value
            local_serial_number = self.settings['system_info'][
                'serial_number'].value
        except:  # noqa
            pass
        # Check if console is out of date and notify user if so.
        if self.prompt:
            local_console_version = parse_version(CONSOLE_VERSION)
            remote_console_version = parse_version(self.newest_console_vers)
            self.console_outdated = remote_console_version > local_console_version

            # we want to warn users using v2 regardless of version logic
            if self.console_outdated or self.is_v2:
                if not self.is_v2:
                    console_outdated_prompt = \
                        prompt.CallbackPrompt(
                            title="Swift Console Outdated",
                            actions=[prompt.close_button],
                        )
                    console_outdated_prompt.text = \
                        "Your console is out of date and may be incompatible\n" + \
                        "with current firmware. We highly recommend upgrading to\n" + \
                        "ensure proper behavior.\n\n" + \
                        "Please visit http://support.swiftnav.com to\n" + \
                        "download the latest version.\n\n" + \
                        "Local Console Version :\n\t" + \
                        "v" + CONSOLE_VERSION + \
                        "\nLatest Console Version :\n\t" + \
                        self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"
                else:
                    console_outdated_prompt = \
                        prompt.CallbackPrompt(
                            title="Swift Console Incompatible",
                            actions=[prompt.close_button],
                        )
                    console_outdated_prompt.text = \
                        "Your console is incompatible with your hardware revision.\n" + \
                        "We highly recommend using a compatible console version\n" + \
                        "to ensure proper behavior.\n\n" + \
                        "Please visit http://support.swiftnav.com to\n" + \
                        "download the latest compatible version.\n\n" + \
                        "Current Hardware revision :\n\t" + \
                        self.piksi_hw_rev + \
                        "\nLast supported Console Version: \n\t" + \
                        self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"

                console_outdated_prompt.run()

            # For timing aesthetics between windows popping up.
            sleep(0.5)

            # Check if firmware is out of date and notify user if so.
            remote_stm_version = self.newest_stm_vers

            self.fw_outdated = remote_stm_version > local_stm_version
            if local_stm_version.startswith('DEV'):
                self.fw_outdated = False

            if self.fw_outdated:
                fw_update_prompt = \
                    prompt.CallbackPrompt(
                        title='Firmware Update',
                        actions=[prompt.close_button]
                    )

                if 'fw' in self.update_dl.index[self.piksi_hw_rev]:
                    fw_update_prompt.text = \
                        "New Piksi firmware available.\n\n" + \
                        "Please use the Update tab to update.\n\n" + \
                        "Newest Firmware Version :\n\t%s\n\n" % \
                        self.update_dl.index[self.piksi_hw_rev]['fw']['version']
                else:
                    fw_update_prompt.text = \
                        "New Piksi firmware available.\n\n" + \
                        "Please use the Update tab to update.\n\n" + \
                        "Newest STM Version :\n\t%s\n\n" % \
                        self.update_dl.index[self.piksi_hw_rev]['stm_fw']['version'] + \
                        "Newest SwiftNAP Version :\n\t%s\n\n" % \
                        self.update_dl.index[self.piksi_hw_rev]['nap_fw']['version']

                fw_update_prompt.run()

        # Check if firmware successfully upgraded and notify user if so.
        if ((self.last_call_fw_version is not None
             and self.last_call_fw_version != local_stm_version)
                and (self.last_call_sn is None or local_serial_number is None
                     or self.last_call_sn == local_serial_number)):
            fw_success_str = "Firmware successfully upgraded from %s to %s." % \
                             (self.last_call_fw_version, local_stm_version)
            print(fw_success_str)
            self._write(fw_success_str)

        # Record firmware version reported each time this callback is called.
        self.last_call_fw_version = local_stm_version
        self.last_call_sn = local_serial_number

    def _get_latest_version_info(self):
        """ Get latest firmware / console version from website. """
        try:
            self.update_dl = UpdateDownloader(root_dir=self.download_directory)
        except RuntimeError:
            self._write(
                "\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n"
            )
            self.update_dl = None
            return

        # Make sure index contains all keys we are interested in.
        try:
            if 'fw' in self.update_dl.index[self.piksi_hw_rev]:
                self.newest_stm_vers = self.update_dl.index[
                    self.piksi_hw_rev]['fw']['version']
            else:
                self.newest_stm_vers = self.update_dl.index[
                    self.piksi_hw_rev]['stm_fw']['version']
                self.newest_nap_vers = self.update_dl.index[
                    self.piksi_hw_rev]['nap_fw']['version']
            self.newest_console_vers = self.update_dl.index[
                self.piksi_hw_rev]['console']['version']
        except KeyError:
            self._write(
                "\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n"
                % INDEX_URL)
            return

    def file_transfer_progress_cb(self, arg):
        new_pcent = float(arg) / float(self.blob_size) * 100
        if new_pcent - self.pcent_complete > 0.1:
            self.pcent_complete = new_pcent
            self.stream.scrollback_write(
                "{:2.1f} % of {:2.1f} MB transferred.".format(
                    self.pcent_complete, self.blob_size * 1e-6))

    def log_cb(self, msg, **kwargs):
        for regex in UPGRADE_WHITELIST:
            if re.match(regex, msg.text):
                text = msg.text.replace("\r", "\n").strip().split("\n")
                if len(text) > 1:
                    # upgrade tool deliminates lines in stoud with \r, we want penultimate line that is complete to show
                    text = text[-2]
                else:
                    # If there is only one line, we show that
                    text = text[-1]
                self.stream.scrollback_write(text)

    def manage_multi_firmware_update(self):
        self.blob_size = float(len(self.stm_fw.blob))
        self.pcent_complete = 0
        # Set up progress dialog and transfer file to Piksi using SBP FileIO
        self._clear_stream()
        self._write(
            "Transferring image to device...\n\n00.0 of {:2.1f} MB trasnferred"
            .format(self.blob_size * 1e-6))
        try:
            FileIO(self.link).write("upgrade.image_set.bin",
                                    self.stm_fw.blob,
                                    progress_cb=self.file_transfer_progress_cb)
        except Exception as e:
            self._write("Failed to transfer image file to Piksi: %s\n" % e)
            self._write("Upgrade Aborted.")
            import traceback
            print(traceback.format_exc())
            return -1

        self.stream.scrollback_write(
            "Image transfer complete: {:2.1f} MB transferred.\n".format(
                self.blob_size * 1e-6))
        # Setup up pulsed progress dialog and commit to flash
        self._write("Committing file to Flash...\n")
        self.link.add_callback(self.log_cb, SBP_MSG_LOG)
        code = shell_command(self.link, "upgrade_tool upgrade.image_set.bin",
                             200)
        self.link.remove_callback(self.log_cb, SBP_MSG_LOG)

        if code != 0:
            self._write('Failed to perform upgrade (code = %d)' % code)
            if code == -255:
                self._write('Shell command timed out.  Please try again.')
            return
        self._write("Upgrade Complete.")
        self._write('Resetting Piksi...')
        self.link(MsgReset(flags=0))

    # Executed in GUI thread, called from Handler.
    def manage_firmware_updates(self, device):
        """
        Update Piksi firmware. Erase entire STM flash (other than bootloader)
        if so directed. Flash NAP only if new firmware is available.
        """
        self.updating = True
        self._write('')
        if not self.is_v2:
            self.manage_multi_firmware_update()
        else:
            self._write(
                'Unable to upgrade piksi v2; please use the last supported v2 console version.'
            )
            self._write("")
        self.updating = False
Ejemplo n.º 18
0
class SourceWidget(Component):

    # The version of this class.  Used for persistence.
    __version__ = 0

    # The actual poly data source widget.
    widget = Instance(tvtk.ThreeDWidget, record=True)

    # Specifies the updation mode of the poly_data attribute.  There
    # are three modes: 1) 'interactive' -- the poly_data attribute is
    # updated as the widget is interacted with, 2) 'semi-interactive'
    # -- poly_data attribute is updated when the traits of the widget
    # change and when the widget interaction is complete, 3)
    # 'non-interactive' -- poly_data is updated only explicitly at
    # users request by calling `object.update_poly_data`.
    update_mode = Trait(
        'interactive',
        TraitPrefixList(['interactive', 'semi-interactive',
                         'non-interactive']),
        desc='the speed at which the poly data is updated')

    # A list of predefined glyph sources that can be used.
    widget_list = List(tvtk.Object, record=False)

    # The poly data that the widget manages.
    poly_data = Instance(tvtk.PolyData, args=())

    ########################################
    # Private traits.

    _first = Bool(True)
    _busy = Bool(False)
    _unpickling = Bool(False)
    _bounds = List

    ########################################
    # View related traits.

    view = View(
        Group(
            Item(name='widget',
                 style='custom',
                 resizable=True,
                 editor=InstanceEditor(name='widget_list')),
            label='Source Widget',
            show_labels=False,
        ),
        resizable=True,
    )

    ######################################################################
    # `Base` interface
    ######################################################################
    def __get_pure_state__(self):
        d = super(SourceWidget, self).__get_pure_state__()
        for attr in ('poly_data', '_unpickling', '_first', '_busy'):
            d.pop(attr, None)
        return d

    def __set_pure_state__(self, state):
        self._unpickling = True
        # First create all the allowed widgets in the widget_list attr.
        handle_children_state(self.widget_list, state.widget_list)
        # Now set their state.
        set_state(self, state, first=['widget_list'], ignore=['*'])
        # Set the widget attr depending on value saved.
        m = [x.__class__.__name__ for x in self.widget_list]
        w_c_name = state.widget.__metadata__['class_name']
        w = self.widget = self.widget_list[m.index(w_c_name)]
        # Set the input.
        if len(self.inputs) > 0:
            self.configure_input(w, self.inputs[0].outputs[0])
        # Fix for the point widget.
        if w_c_name == 'PointWidget':
            w.place_widget()
        # Set state of rest of the attributes ignoring the widget_list.
        set_state(self, state, ignore=['widget_list'])
        # Some widgets need some cajoling to get their setup right.
        w.update_traits()
        if w_c_name == 'PlaneWidget':
            w.origin = state.widget.origin
            w.normal = state.widget.normal
            w.center = state.widget.center
            w.update_placement()
            w.get_poly_data(self.poly_data)
        elif w_c_name == 'SphereWidget':
            # XXX: This hack is necessary because the sphere widget
            # does not update its poly data even when its ivars are
            # set (plus it does not have an update_placement method
            # which is a bug).  So we force this by creating a similar
            # sphere source and copy its output.
            s = tvtk.SphereSource(center=w.center,
                                  radius=w.radius,
                                  theta_resolution=w.theta_resolution,
                                  phi_resolution=w.phi_resolution,
                                  lat_long_tessellation=True)
            s.update()
            self.poly_data.shallow_copy(s.output)
        else:
            w.get_poly_data(self.poly_data)
        self._unpickling = False
        # Set the widgets trait so that the widget is rendered if needed.
        self.widgets = [w]

    ######################################################################
    # `Component` interface
    ######################################################################
    def setup_pipeline(self):
        """Override this method so that it *creates* the tvtk
        pipeline.

        This method is invoked when the object is initialized via
        `__init__`.  Note that at the time this method is called, the
        tvtk data pipeline will *not* yet be setup.  So upstream data
        will not be available.  The idea is that you simply create the
        basic objects and setup those parts of the pipeline not
        dependent on upstream sources and filters.  You should also
        set the `actors` attribute up at this point.
        """
        # Setup the glyphs.
        sources = [
            tvtk.SphereWidget(theta_resolution=8, phi_resolution=6),
            tvtk.LineWidget(clamp_to_bounds=False),
            tvtk.PlaneWidget(),
            tvtk.PointWidget(outline=False,
                             x_shadows=False,
                             y_shadows=False,
                             z_shadows=False),
        ]
        self.widget_list = sources
        # The 'widgets' trait is set in the '_widget_changed' handler.
        self.widget = sources[0]

        for s in sources:
            self._connect(s)

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when any of the inputs
        sends a `pipeline_changed` event.
        """
        if len(self.inputs) == 0:
            return
        inp = self.inputs[0].outputs[0]
        w = self.widget
        self.configure_input(w, inp)
        dsh = DataSetHelper(self.inputs[0].outputs[0])
        b = dsh.get_bounds()
        self._bounds = list(b)
        if self._first:
            w.place_widget(b)
            self._first = False

        # If the dataset is effectively 2D switch to using the line
        # widget since that works best.
        l = [(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]
        max_l = max(l)
        for i, x in enumerate(l):
            if x / max_l < 1.0e-6:
                w = self.widget = self.widget_list[1]
                w.clamp_to_bounds = True
                w.align = ['z_axis', 'z_axis', 'y_axis'][i]
                break

        # Set our output.
        w.get_poly_data(self.poly_data)
        self.outputs = [self.poly_data]

        self.pipeline_changed = True

    def update_data(self):
        """Override this method so that it flushes the vtk pipeline if
        that is necessary.

        This method is invoked (automatically) when any of the inputs
        sends a `data_changed` event.
        """
        self.data_changed = True

    ######################################################################
    # `SourceWidget` interface
    ######################################################################
    def update_poly_data(self):
        self.widget.get_poly_data(self.poly_data)

    ######################################################################
    # Non-public traits.
    ######################################################################
    def _widget_changed(self, value):
        # If we are being unpickled do nothing.
        if self._unpickling:
            return
        if value not in self.widget_list:
            classes = [o.__class__ for o in self.widget_list]
            vc = value.__class__
            self._connect(value)
            if vc in classes:
                self.widget_list[classes.index(vc)] = value
            else:
                self.widget_list.append(value)

        recorder = self.recorder
        if recorder is not None:
            idx = self.widget_list.index(value)
            name = recorder.get_script_id(self)
            lhs = '%s.widget' % name
            rhs = '%s.widget_list[%d]' % (name, idx)
            recorder.record('%s = %s' % (lhs, rhs))

        if len(self.inputs) > 0:
            self.configure_input(value, self.inputs[0].outputs[0])
            value.place_widget(self._bounds)

        value.on_trait_change(self.render)
        self.widgets = [value]

    def _update_mode_changed(self, value):
        if value in ['interactive', 'semi-interactive']:
            self.update_poly_data()
            self.render()

    def _on_interaction_event(self, obj, event):
        if (not self._busy) and (self.update_mode == 'interactive'):
            self._busy = True
            self.update_poly_data()
            self._busy = False

    def _on_widget_trait_changed(self):
        if (not self._busy) and (self.update_mode != 'non-interactive'):
            self._busy = True
            # This render call forces any changes to the trait to be
            # rendered only then will updating the poly data make
            # sense.
            self.render()
            self.update_poly_data()
            self._busy = False

    def _on_alignment_set(self):
        w = self.widget
        w.place_widget(self._bounds)
        w.update_traits()

    def _connect(self, obj):
        """Wires up all the event handlers."""
        obj.add_observer('InteractionEvent', self._on_interaction_event)
        if isinstance(obj, tvtk.PlaneWidget):
            obj.on_trait_change(self._on_alignment_set, 'normal_to_x_axis')
            obj.on_trait_change(self._on_alignment_set, 'normal_to_y_axis')
            obj.on_trait_change(self._on_alignment_set, 'normal_to_z_axis')
        elif isinstance(obj, tvtk.LineWidget):
            obj.on_trait_change(self._on_alignment_set, 'align')

        # Setup the widgets colors.
        fg = (1, 1, 1)
        if self.scene is not None:
            fg = self.scene.foreground
        self._setup_widget_colors(obj, fg)

        obj.on_trait_change(self._on_widget_trait_changed)
        obj.on_trait_change(self.render)

    def _setup_widget_colors(self, widget, color):
        trait_names = widget.trait_names()
        props = [
            x for x in trait_names if 'property' in x and 'selected' not in x
        ]
        sel_props = [
            x for x in trait_names if 'property' in x and 'selected' in x
        ]
        for p in props:
            setattr(getattr(widget, p), 'color', color)
            setattr(getattr(widget, p), 'line_width', 2)
        for p in sel_props:
            # Set the selected color to 'red'.
            setattr(getattr(widget, p), 'color', (1, 0, 0))
            setattr(getattr(widget, p), 'line_width', 2)
        self.render()

    def _foreground_changed_for_scene(self, old, new):
        # Change the default color for the actor.
        for w in self.widget_list:
            self._setup_widget_colors(w, new)
        self.render()

    def _scene_changed(self, old, new):
        super(SourceWidget, self)._scene_changed(old, new)
        self._foreground_changed_for_scene(None, new.foreground)
Ejemplo n.º 19
0
class PolyDataNormals(Component):
    # The version of this class.  Used for persistence.
    __version__ = 0

    # The filter that generates the normals.
    filter = Instance(tvtk.PolyDataNormals,
                      args=(),
                      kw={'feature_angle': 45.0},
                      record=True)

    ########################################
    # The component's view

    _filter_group = Group(Item(name='feature_angle'))

    view = View(
        Group(Item(name='filter',
                   style='custom',
                   editor=InstanceEditor(view=View(_filter_group))),
              show_labels=False))

    ######################################################################
    # `Component` interface
    ######################################################################
    def setup_pipeline(self):
        """Override this method so that it *creates* its tvtk
        pipeline.

        This method is invoked when the object is initialized via
        `__init__`.  Note that at the time this method is called, the
        tvtk data pipeline will *not* yet be setup.  So upstream data
        will not be available.  The idea is that you simply create the
        basic objects and setup those parts of the pipeline not
        dependent on upstream sources and filters.
        """
        self.filter.on_trait_change(self.update_data)

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when the input fires a
        `pipeline_changed` event.
        """
        if (len(self.inputs) == 0) or \
               (len(self.inputs[0].outputs) == 0):
            return
        f = self.filter
        input = self.inputs[0].outputs[0]
        self.configure_input(f, convert_to_poly_data(input))
        f.update()
        self.outputs = [f]

    def update_data(self):
        """Override this method to do what is necessary when upstream
        data changes.

        This method is invoked (automatically) when any of the inputs
        sends a `data_changed` event.
        """
        self.data_changed = True

    def has_output_port(self):
        """ The filter has an output port."""
        return True

    def get_output_object(self):
        """ Returns the output port."""
        return self.filter.output_port
Ejemplo n.º 20
0
class CvuGUI(ErrorHandler, DatasetViewportInterface):
    controller = Instance(Controller)

    def _mayavi_port_default(self):
        return Viewport(self.controller.ds_orig, view_type='3D Brain')

    def _matrix_port_default(self):
        return Viewport(self.controller.ds_orig, view_type='Connection Matrix')

    def _circle_port_default(self):
        return Viewport(self.controller.ds_orig, view_type='Circular plot')

    options_window = Instance(dialogs.InteractiveSubwindow)
    adjmat_chooser_window = Instance(dialogs.InteractiveSubwindow)
    parcellation_chooser_window = Instance(dialogs.InteractiveSubwindow)
    tractography_chooser_window = Instance(dialogs.InteractiveSubwindow)
    node_chooser_window = Instance(dialogs.InteractiveSubwindow)
    module_chooser_window = Instance(dialogs.InteractiveSubwindow)
    module_customizer_window = Instance(dialogs.InteractiveSubwindow)
    graph_theory_window = Instance(dialogs.InteractiveSubwindow)
    configure_scalars_window = Instance(dialogs.InteractiveSubwindow)
    save_snapshot_window = Instance(dialogs.InteractiveSubwindow)
    make_movie_window = Instance(dialogs.InteractiveSubwindow)
    really_overwrite_file_window = Instance(dialogs.InteractiveSubwindow)
    calculate_window = Instance(dialogs.InteractiveSubwindow)
    color_legend_window = Instance(dialogs.InteractiveSubwindow)

    #load_tractography_window
    load_standalone_matrix_window = Instance(dialogs.InteractiveSubwindow)

    select_node_button = Button('Choose node')
    display_all_button = Button('Reset Displays')
    graph_theory_button = Button('Show statistics')
    calculate_button = Button('Calculate stats')
    #load_module_button = 			Button('Load module')
    select_module_button = Button('View module')
    custom_module_button = Button('Custom subset')
    display_scalars_button = Button('Show scalars')
    #load_scalars_button = 			Button('Load scalars')
    load_standalone_button = Button('Load stats')
    load_adjmat_button = Button('Load an adjacency matrix')
    #force_render_button = 			Button('Force render')
    color_legend_button = Button('Color legend')
    load_parcellation_button = Button('Load a parcellation')
    options_button = Button('Options')
    controller_button = Button('Manage views')
    load_tractography_button = Button('Load tractography')
    save_snapshot_button = Button('Take snapshot')
    make_movie_button = Button
    currently_making_movie = Bool(False)
    mk_movie_lbl = Property(depends_on='currently_making_movie')

    def _get_mk_movie_lbl(self):
        return 'Stop movie' if self.currently_making_movie else 'Make movie'

    about_button = Button('About')
    manage_views_button = Button('Manage views')

    python_shell = Dict

    traits_view = View(
        VSplit(
            HSplit(
                Item(
                    name='mayavi_port',
                    height=500,
                    width=500,
                    editor=InstanceEditor(view='mayavi_view'),
                    show_label=False,
                    style='custom',
                    resizable=True,
                ),
                Item(
                    name='matrix_port',
                    height=500,
                    width=500,
                    editor=InstanceEditor(view='matrix_view'),
                    show_label=False,
                    style='custom',
                    resizable=True,
                ),
                Group(
                    Spring(),
                    Item(name='select_node_button'),
                    Item(name='display_all_button'),
                    Item(name='color_legend_button'),
                    Spring(),
                    Item(name='calculate_button'),
                    Item(name='load_standalone_button'),
                    Item(name='graph_theory_button'),
                    Item(name='display_scalars_button'),
                    Item(name='select_module_button'),
                    Item(name='custom_module_button'),
                    Spring(),
                    #Item(name='force_render_button'),
                    show_labels=False,
                )),
            HSplit(
                Item(
                    name='circle_port',
                    height=500,
                    width=500,
                    editor=InstanceEditor(view='circle_view'),
                    show_label=False,
                    style='custom',
                    resizable=True,
                ),
                Group(
                    HSplit(
                        Item(name='load_parcellation_button', ),
                        Item(name='load_adjmat_button', ),
                        Item(name='load_tractography_button', ),
                        show_labels=False,
                    ),
                    HSplit(
                        Item(name='save_snapshot_button'),
                        Item(name='make_movie_button',
                             editor=ButtonEditor(label_value='mk_movie_lbl')),
                        Item(name='options_button', ),
                        Item(name='controller_button', ),
                        Item(name='about_button'),
                        show_labels=False,
                    ),
                    HSplit(
                        Item(name='python_shell',
                             editor=ShellEditor(),
                             height=450,
                             show_label=False), ),
                ),
            ),
        ),
        resizable=True,
        title="Connectome Visualization Utility")

    def __init__(self, sample_data, sample_metadata, quiet=False, **kwargs):
        super(HasTraits, self).__init__(quiet=quiet, **kwargs)
        ctrl = self.controller = Controller(self, sample_data, sample_metadata)
        ds_orig = self.controller.ds_orig

        #these dialogs exist strictly in the gui and have no control item
        #they do not extend interactivesubwindow
        self.error_dialog_window = dialogs.ErrorDialogWindow()
        self.warning_dialog_window = dialogs.WarningDialogWindow()
        self.about_window = dialogs.AboutWindow()
        self.really_overwrite_file_window = dialogs.ReallyOverwriteFileWindow()

        self.options_window = dialogs.OptionsWindow(ds_orig.opts, ctrl)
        self.configure_scalars_window = dialogs.ConfigureScalarsWindow(
            ds_orig.scalar_display_settings, ctrl)

        self.calculate_window = dialogs.CalculateWindow(
            ctrl.options_db.calculate_parameters, ctrl)

        self.adjmat_chooser_window = dialogs.AdjmatChooserWindow(
            ctrl.options_db.adjmat_chooser_parameters, ctrl)
        self.parcellation_chooser_window = dialogs.ParcellationChooserWindow(
            ctrl.options_db.parcellation_chooser_parameters, ctrl)
        self.tractography_chooser_window = dialogs.TractographyChooserWindow(
            ctrl.options_db.tractography_chooser_parameters, ctrl)
        self.load_standalone_matrix_window = dialogs.LoadGeneralMatrixWindow(
            ctrl.options_db.general_matrix_chooser_parameters, ctrl)
        self.node_chooser_window = dialogs.NodeChooserWindow(
            ctrl.options_db.node_chooser_parameters, ctrl)
        self.module_chooser_window = dialogs.ModuleChooserWindow(
            ctrl.options_db.module_chooser_parameters, ctrl)
        self.module_customizer_window = dialogs.ModuleCustomizerWindow(
            ctrl.options_db.module_customizer_parameters, ctrl)
        self.graph_theory_window = dialogs.GraphTheoryWindow(
            ctrl.options_db.graph_theory_parameters, ctrl)
        self.color_legend_window = dialogs.ColorLegendWindow(
            ctrl.options_db.color_legend_parameters, ctrl)
        self.save_snapshot_window = dialogs.SaveSnapshotWindow(
            ctrl.options_db.snapshot_parameters, ctrl)
        self.make_movie_window = dialogs.MakeMovieWindow(
            ctrl.options_db.make_movie_parameters, ctrl)

        self.panel_name = 'base_gui'

    def error_dialog(self, message):
        self.error_dialog_window.stacktrace = None
        _, _, tb = sys.exc_info()
        if tb is not None:
            self.error_dialog_window.stacktrace = tb
        self.error_dialog_window.error = message
        self.error_dialog_window.edit_traits()

    def warning_dialog(self, message):
        self.warning_dialog_window.warning = message
        self.warning_dialog_window.edit_traits()

    def reset_controls(self, ds_match):
        for window in (
                self.load_standalone_matrix_window,
                self.adjmat_chooser_window,
                self.node_chooser_window,
                self.parcellation_chooser_window,
                self.module_chooser_window,
                self.module_customizer_window,
                self.graph_theory_window,
                self.save_snapshot_window,
                self.make_movie_window,
                self.calculate_window,
                self.color_legend_window,
                self.configure_scalars_window,
                self.options_window,
        ):
            if window.ctl.ds_ref is ds_match or window.ctl.ds_ref is None:
                window._current_dataset_list = [self.controller.ds_orig]

    ######################################################################
    # BUTTONS AND INTERACTIONS
    ######################################################################

    def _display_all_button_fired(self):
        for ds in self.controller.ds_instances.values():
            ds.display_all()

    def _options_button_fired(self):
        self.options_window.finished = False
        self.options_window.edit_traits()

    def _load_parcellation_button_fired(self):
        self.parcellation_chooser_window.finished = False
        self.parcellation_chooser_window.edit_traits()

    @on_trait_change('parcellation_chooser_window:notify')
    def _load_parcellation_check(self):
        pcw = self.parcellation_chooser_window
        if not pcw.finished: return
        if pcw.ctl.new_dataset:
            if pcw.ctl.new_dataset_name == '':
                self.error_dialog('Must specify a dataset name!')
                return
            elif pcw.ctl.new_dataset_name in self.controller.ds_instances:
                self.error_dialog('Dataset name is not unique')
                return
            else:
                ds_name = pcw.ctl.new_dataset_name
                import preprocessing
                parc_struct = preprocessing.process_parc(pcw.ctl, self)
                if parc_struct is None: return  #preprocessing errored out

                lab_pos, labnam, srf, labv, subject_name, parc_name = parc_struct

                display_metadata = DisplayMetadata(subject_name=subject_name,
                                                   parc_name=parc_name,
                                                   adj_filename='')
                ds = Dataset(ds_name, lab_pos, labnam, srf, labv, gui=self)
                self.controller.add_dataset(ds, display_metadata)
        else:
            import preprocessing
            parc_struct = preprocessing.process_parc(pcw.ctl, self)
            if parc_struct is None: return

            lab_pos, labnam, srf, labv, subject_name, parc_name = parc_struct
            pcw.ctl.ds_ref._load_parc(lab_pos, labnam, srf, labv)
            self.controller.update_display_metadata(pcw.ctl.ds_ref.name,
                                                    subject_name=subject_name,
                                                    parc_name=parc_name)

            #find the viewports that were previously holding this scene
            #find_dataset_views returns a DatasetViewportInterface object
            #with references to the viewports (source in viewport.py)
            ds_interface = self.controller.find_dataset_views(pcw.ctl.ds_ref)
            ds_interface.mayavi_port = Viewport(pcw.ctl.ds_ref)
            ds_interface.matrix_port = Viewport(pcw.ctl.ds_ref)
            ds_interface.circle_port = Viewport(pcw.ctl.ds_ref)

    def _load_adjmat_button_fired(self):
        self.adjmat_chooser_window.finished = False
        self.adjmat_chooser_window.edit_traits()

    @on_trait_change('adjmat_chooser_window:notify')
    def _load_adjmat_check(self):
        acw = self.adjmat_chooser_window
        if not acw.finished: return

        import preprocessing as pp
        adj_struct = pp.process_adj(acw.ctl, self)
        if adj_struct is None: return  #preprocessing returned an error

        adj, soft_max_edges, adj_filename = adj_struct
        acw.ctl.ds_ref._load_adj(adj, soft_max_edges, acw.ctl.require_ls,
                                 acw.ctl.suppress_extra_rois)
        self.controller.update_display_metadata(acw.ctl.ds_ref.name,
                                                adj_filename=adj_filename)

    def _load_tractography_button_fired(self):
        self.tractography_chooser_window.finished = False
        self.tractography_chooser_window.edit_traits()

    @on_trait_change('tractography_chooser_window:notify')
    def _load_tractography_check(self):
        tcw = self.tractography_chooser_window
        if not tcw.finished: return
        tcw.ctl.ds_ref.load_tractography(tcw.ctl)

    def _load_standalone_button_fired(self):
        self.load_standalone_matrix_window.finished = False
        self.load_standalone_matrix_window.edit_traits()

    @on_trait_change('load_standalone_matrix_window:notify')
    def _load_standalone_check(self):
        lsmw = self.load_standalone_matrix_window
        if not lsmw.finished: return
        lsmw.ctl.ds_ref.load_modules_or_scalars(lsmw.ctl)

    def _display_scalars_button_fired(self):
        #more checking required.  should make sure scalars exist first.
        csw = self.configure_scalars_window
        csw.finished = False
        #csw.ctl.reset_configuration()
        self.configure_scalars_window.edit_traits()

    @on_trait_change('configure_scalars_window:notify')
    def _display_scalars_check(self):
        csw = self.configure_scalars_window

        if not csw.finished or (not any(
            (csw.ctl.node_color, csw.ctl.surf_color, csw.ctl.node_size,
             csw.ctl.circle, csw.ctl.connmat))):
            return
        csw.ctl.ds_ref.display_scalars()

    def _select_node_button_fired(self):
        self.node_chooser_window.finished = False
        self.node_chooser_window.edit_traits()

    @on_trait_change('node_chooser_window:notify')
    def _select_node_check(self):
        ncw = self.node_chooser_window
        if not ncw.finished: return
        ncw.ctl.ds_ref.display_node(ncw.ctl.cur_node)

    def _calculate_button_fired(self):
        cw = self.calculate_window
        cw.finished = False
        cw.edit_traits()

    @on_trait_change('calculate_window:notify')
    def _calculation_check(self):
        cw = self.calculate_window
        if not cw.finished: return

        if cw.ctl.ds_ref.adj is None:
            self.error_dialog("There is no adjacency matrix loaded")
            return

        if cw.ctl.thresh_type == 'abs':
            thres = cw.ctl.athresh
        elif cw.ctl.thresh_type == 'prop':
            if cw.ctl.pthresh == 0.:
                thres = -np.inf
            else:
                thres = cw.ctl.ds_ref.adjdat[int(
                    round(cw.ctl.pthresh * cw.ctl.ds_ref.nr_edges - 1))]

        if cw.ctl.calculation_type == 'modules':
            cw.ctl.ds_ref.calculate_modules(thres)
            cw.ctl.ds_ref.display_multi_module()
        elif cw.ctl.calculation_type == 'statistics':
            (Thread(target=cw.ctl.ds_ref.calculate_graph_stats,
                    args=(thres, ))).start()

    def _select_module_button_fired(self):
        self.module_chooser_window.finished = False
        self.module_chooser_window.edit_traits()

    @on_trait_change('module_chooser_window:notify')
    def _select_module_check(self):
        mcw = self.module_chooser_window
        if not mcw.finished or mcw.ctl.cur_mod == -1: return
        else: mcw.ctl.ds_ref.display_module(mcw.ctl.cur_mod)

    def _custom_module_button_fired(self):
        self.module_customizer_window.finished = False
        self.module_customizer_window.edit_traits()

    @on_trait_change('module_customizer_window:notify')
    def _module_customizer_check(self):
        mcw = self.module_customizer_window
        if not mcw.finished: return
        try:
            mcw.ctl._index_convert()
        except ValueError:
            self.error_dialog('Internal error: bad index conversion')
            return
        mcw.ctl.ds_ref.custom_module = mcw.ctl.return_module
        mcw.ctl.ds_ref.display_module('custom')

    def _graph_theory_button_fired(self):
        #more checking required. should make sure stats exist first
        self.graph_theory_window.finished = False
        self.graph_theory_window.edit_traits()

    def _color_legend_button_fired(self):
        self.color_legend_window.edit_traits()

    def _save_snapshot_button_fired(self):
        self.save_snapshot_window.finished = False
        self.save_snapshot_window.edit_traits()

    @on_trait_change('save_snapshot_window:notify')
    def _save_snapshot_check(self):
        ssw = self.save_snapshot_window
        if not ssw.finished: return

        save_continuation = ssw.ctl.ds_ref.snapshot(ssw.ctl)
        self.process_save_continuation(ssw.ctl.savefile, save_continuation)

        #elif not ssw.ctl.savefile:
        #	self.error_dialog('You must specify a filename to save to')
        #	return
        #else:
        #	save_continuation = ssw.ctl.ds_ref.snapshot(ssw.ctl)
        #	if not os.path.exists(os.path.dirname(ssw.ctl.savefile)):
        #		self.error_dialog('Bad path specified. Check for typo?')
        #	elif not os.path.exists(ssw.ctl.savefile):
        #		save_continuation()
        #	else:
        #		rofw = self.really_overwrite_file_window
        #		rofw.save_continuation = save_continuation
        #		rofw.finished=False
        #		rofw.edit_traits()

    def _make_movie_button_fired(self):
        if not self.currently_making_movie:
            self.make_movie_window.finished = False
            self.make_movie_window.edit_traits()
        else:
            self.currently_making_movie = False
            mmw = self.make_movie_window
            mmw.ctl.ds_ref.make_movie_finish(mmw.ctl)

    @on_trait_change('make_movie_window:notify')
    def make_movie_check(self):
        mmw = self.make_movie_window
        if not mmw.finished: return

        movie_continuation = mmw.ctl.ds_ref.make_movie(mmw.ctl)

        def save_continuation():
            self.currently_making_movie = True
            movie_continuation()

        self.process_save_continuation(mmw.ctl.savefile, save_continuation)

    @on_trait_change('really_overwrite_file_window:notify')
    def _really_overwrite_file_check(self):
        rofw = self.really_overwrite_file_window
        #if the user clicks ok, call the save continuation
        if rofw.finished: rofw.save_continuation()
        #otherwise, dont do anything

    def process_save_continuation(self, filename, save_continuation):
        if not filename:
            self.error_dialog("No save file specified")
            return
        elif not os.path.exists(os.path.dirname(filename)):
            self.error_dialog("Bad save file specified. Check for typos.")
            return
        elif not os.path.exists(filename):
            save_continuation()
        else:
            rofw = self.really_overwrite_file_window
            rofw.save_continuation = save_continuation
            rofw.finished = False
            rofw.edit_traits()

    def _controller_button_fired(self):
        self.controller.viewport_manager.edit_traits()

    def _about_button_fired(self):
        self.about_window.edit_traits()
Ejemplo n.º 21
0
class Volume(Module):
    """The Volume module visualizes scalar fields using volumetric
    visualization techniques.  This supports ImageData and
    UnstructuredGrid data.  It also supports the FixedPointRenderer
    for ImageData.  However, the performance is slow so your best bet
    is probably with the ImageData based renderers.
    """

    # The version of this class.  Used for persistence.
    __version__ = 0

    volume_mapper_type = DEnum(values_name='_mapper_types',
                               desc='volume mapper to use')

    ray_cast_function_type = DEnum(values_name='_ray_cast_functions',
                                   desc='Ray cast function to use')

    volume = ReadOnly

    volume_mapper = Property(record=True)

    volume_property = Property(record=True)

    ray_cast_function = Property(record=True)

    lut_manager = Instance(VolumeLUTManager,
                           args=(),
                           allow_none=False,
                           record=True)

    input_info = PipelineInfo(datasets=['image_data', 'unstructured_grid'],
                              attribute_types=['any'],
                              attributes=['scalars'])

    ########################################
    # View related code.

    update_ctf = Button('Update CTF')

    view = View(Group(Item(name='_volume_property',
                           style='custom',
                           editor=CustomEditor(gradient_editor_factory),
                           resizable=True),
                      Item(name='update_ctf'),
                      label='CTF',
                      show_labels=False),
                Group(
                    Item(name='volume_mapper_type'),
                    Group(Item(name='_volume_mapper',
                               style='custom',
                               resizable=True),
                          show_labels=False),
                    Item(name='ray_cast_function_type'),
                    Group(Item(name='_ray_cast_function',
                               enabled_when='len(_ray_cast_functions) > 0',
                               style='custom',
                               resizable=True),
                          show_labels=False),
                    label='Mapper',
                ),
                Group(Item(name='_volume_property',
                           style='custom',
                           resizable=True),
                      label='Property',
                      show_labels=False),
                Group(Item(name='volume',
                           style='custom',
                           editor=InstanceEditor(),
                           resizable=True),
                      label='Volume',
                      show_labels=False),
                Group(Item(name='lut_manager', style='custom', resizable=True),
                      label='Legend',
                      show_labels=False),
                resizable=True)

    ########################################
    # Private traits
    _volume_mapper = Instance(tvtk.AbstractVolumeMapper)
    _volume_property = Instance(tvtk.VolumeProperty)
    _ray_cast_function = Instance(tvtk.Object)

    _mapper_types = List(Str, [
        'TextureMapper2D',
        'RayCastMapper',
    ])

    _available_mapper_types = List(Str)

    _ray_cast_functions = List(Str)

    current_range = Tuple

    # The color transfer function.
    _ctf = Instance(ColorTransferFunction)
    # The opacity values.
    _otf = Instance(PiecewiseFunction)

    ######################################################################
    # `object` interface
    ######################################################################
    def __get_pure_state__(self):
        d = super(Volume, self).__get_pure_state__()
        d['ctf_state'] = save_ctfs(self._volume_property)
        for name in ('current_range', '_ctf', '_otf'):
            d.pop(name, None)
        return d

    def __set_pure_state__(self, state):
        self.volume_mapper_type = state['_volume_mapper_type']
        state_pickler.set_state(self, state, ignore=['ctf_state'])
        ctf_state = state['ctf_state']
        ctf, otf = load_ctfs(ctf_state, self._volume_property)
        self._ctf = ctf
        self._otf = otf
        self._update_ctf_fired()

    ######################################################################
    # `Module` interface
    ######################################################################
    def start(self):
        super(Volume, self).start()
        self.lut_manager.start()

    def stop(self):
        super(Volume, self).stop()
        self.lut_manager.stop()

    def setup_pipeline(self):
        """Override this method so that it *creates* the tvtk
        pipeline.
        """
        v = self.volume = tvtk.Volume()
        vp = self._volume_property = tvtk.VolumeProperty()

        self._ctf = ctf = default_CTF(0, 255)
        self._otf = otf = default_OTF(0, 255)
        vp.set_color(ctf)
        vp.set_scalar_opacity(otf)
        vp.shade = True
        vp.interpolation_type = 'linear'
        v.property = vp

        v.on_trait_change(self.render)
        vp.on_trait_change(self.render)

        available_mappers = find_volume_mappers()
        if is_volume_pro_available():
            self._mapper_types.append('VolumeProMapper')
            available_mappers.append('VolumeProMapper')

        self._available_mapper_types = available_mappers
        if 'FixedPointVolumeRayCastMapper' in available_mappers:
            self._mapper_types.append('FixedPointVolumeRayCastMapper')

        self.actors.append(v)

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when any of the inputs
        sends a `pipeline_changed` event.
        """
        mm = self.module_manager
        if mm is None:
            return

        input = mm.source.outputs[0]

        ug = hasattr(tvtk, 'UnstructuredGridVolumeMapper')
        if ug:
            if not input.is_a('vtkImageData') \
                   and not input.is_a('vtkUnstructuredGrid'):
                error('Volume rendering only works with '\
                      'StructuredPoints/ImageData/UnstructuredGrid datasets')
                return
        elif not input.is_a('vtkImageData'):
            error('Volume rendering only works with '\
                  'StructuredPoints/ImageData datasets')
            return

        self._setup_mapper_types()
        self._setup_current_range()
        self._volume_mapper_type_changed(self.volume_mapper_type)
        self._update_ctf_fired()
        self.pipeline_changed = True

    def update_data(self):
        """Override this method so that it flushes the vtk pipeline if
        that is necessary.

        This method is invoked (automatically) when any of the inputs
        sends a `data_changed` event.
        """
        self._setup_mapper_types()
        self._setup_current_range()
        self._update_ctf_fired()
        self.data_changed = True

    ######################################################################
    # Non-public methods.
    ######################################################################
    def _setup_mapper_types(self):
        """Sets up the mapper based on input data types.
        """
        input = self.module_manager.source.outputs[0]
        if input.is_a('vtkUnstructuredGrid'):
            if hasattr(tvtk, 'UnstructuredGridVolumeMapper'):
                check = [
                    'UnstructuredGridVolumeZSweepMapper',
                    'UnstructuredGridVolumeRayCastMapper',
                ]
                mapper_types = []
                for mapper in check:
                    if mapper in self._available_mapper_types:
                        mapper_types.append(mapper)
                if len(mapper_types) == 0:
                    mapper_types = ['']
                self._mapper_types = mapper_types
                return
        else:
            if input.point_data.scalars.data_type not in \
               [vtkConstants.VTK_UNSIGNED_CHAR,
                vtkConstants.VTK_UNSIGNED_SHORT]:
                if 'FixedPointVolumeRayCastMapper' \
                       in self._available_mapper_types:
                    self._mapper_types = ['FixedPointVolumeRayCastMapper']
                else:
                    error('Available volume mappers only work with \
                    unsigned_char or unsigned_short datatypes')
            else:
                mapper_types = ['TextureMapper2D', 'RayCastMapper']
                check = ['FixedPointVolumeRayCastMapper', 'VolumeProMapper']
                for mapper in check:
                    if mapper in self._available_mapper_types:
                        mapper_types.append(mapper)
                self._mapper_types = mapper_types

    def _setup_current_range(self):
        mm = self.module_manager
        # Set the default name and range for our lut.
        lm = self.lut_manager
        slm = mm.scalar_lut_manager
        lm.set(default_data_name=slm.default_data_name,
               default_data_range=slm.default_data_range)

        # Set the current range.
        input = mm.source.outputs[0]
        sc = input.point_data.scalars
        if sc is not None:
            rng = sc.range
        else:
            error('No scalars in input data!')
            rng = (0, 255)

        if self.current_range != rng:
            self.current_range = rng

    def _get_volume_mapper(self):
        return self._volume_mapper

    def _get_volume_property(self):
        return self._volume_property

    def _get_ray_cast_function(self):
        return self._ray_cast_function

    def _volume_mapper_type_changed(self, value):
        mm = self.module_manager
        if mm is None:
            return

        old_vm = self._volume_mapper
        if old_vm is not None:
            old_vm.on_trait_change(self.render, remove=True)

        if value == 'RayCastMapper':
            new_vm = tvtk.VolumeRayCastMapper()
            self._volume_mapper = new_vm
            self._ray_cast_functions = [
                'RayCastCompositeFunction', 'RayCastMIPFunction',
                'RayCastIsosurfaceFunction'
            ]
            new_vm.volume_ray_cast_function = tvtk.VolumeRayCastCompositeFunction(
            )
        elif value == 'TextureMapper2D':
            new_vm = tvtk.VolumeTextureMapper2D()
            self._volume_mapper = new_vm
            self._ray_cast_functions = ['']
        elif value == 'VolumeProMapper':
            new_vm = tvtk.VolumeProMapper()
            self._volume_mapper = new_vm
            self._ray_cast_functions = ['']
        elif value == 'FixedPointVolumeRayCastMapper':
            new_vm = tvtk.FixedPointVolumeRayCastMapper()
            self._volume_mapper = new_vm
            self._ray_cast_functions = ['']
        elif value == 'UnstructuredGridVolumeRayCastMapper':
            new_vm = tvtk.UnstructuredGridVolumeRayCastMapper()
            self._volume_mapper = new_vm
            self._ray_cast_functions = ['']
        elif value == 'UnstructuredGridVolumeZSweepMapper':
            new_vm = tvtk.UnstructuredGridVolumeZSweepMapper()
            self._volume_mapper = new_vm
            self._ray_cast_functions = ['']

        new_vm.input = mm.source.outputs[0]
        self.volume.mapper = new_vm
        new_vm.on_trait_change(self.render)

    def _update_ctf_fired(self):
        set_lut(self.lut_manager.lut, self._volume_property)
        self.render()

    def _current_range_changed(self, old, new):
        rescale_ctfs(self._volume_property, new)
        self.render()

    def _ray_cast_function_type_changed(self, old, new):
        rcf = self.ray_cast_function
        if len(old) > 0:
            rcf.on_trait_change(self.render, remove=True)

        if len(new) > 0:
            new_rcf = getattr(tvtk, 'Volume%s' % new)()
            new_rcf.on_trait_change(self.render)
            self._volume_mapper.volume_ray_cast_function = new_rcf
            self._ray_cast_function = new_rcf
        else:
            self._ray_cast_function = None

        self.render()

    def _scene_changed(self, old, new):
        super(Volume, self)._scene_changed(old, new)
        self.lut_manager.scene = new
Ejemplo n.º 22
0
class MainUi(HasTraits):
    """Main application class"""
    dsc = DatasetContainer()
    # en_advanced = Bool(False)
    win_handle = Any()
    splash = None

    # Object representating the basic stat
    basic_stat = Instance(BasicStatPluginController)
    # Object representing the PCA and the GUI tab
    pca = Instance(PcaPluginController)
    # Object representing the Prefmap and the GUI tab
    prefmap = Instance(PrefmapPluginController)
    # Object representing the PlsrPcr and the GUI tab
    plscr = Instance(PlsrPcrPluginController)
    # Object representing the Conjoint and the GUI tab
    conjoint = Instance(ConjointPluginController)
    # Object representing the IndDiff and the GUI tab
    ind_diff = Instance(IndDiffPluginController)

    # Create an action that open dialog for dataimport
    import_action = Action(name='Add &Data set', action='import_data')
    # Create an action that exits the application.
    exit_action = Action(name='E&xit', action='_on_close')
    about_action = Action(name='&About', action='view_about')
    user_manual_action = Action(name='&User manual', action='view_user_manual')
    close_action = Action(name='&Remove Data sets', action='_close_ds')
    advanced_action = Action(name='&Advanced settings',
                             checked_when='en_advanced',
                             style='toggle',
                             action='_toggle_advanced')

    def _basic_stat_default(self):
        basic_statisitc = BasicStatCalcContainer(dsc=self.dsc)
        return BasicStatPluginController(basic_statisitc)

    def _pca_default(self):
        pca = PcaCalcContainer(dsc=self.dsc)
        return PcaPluginController(pca)

    def _prefmap_default(self):
        prefmap = PrefmapCalcContainer(dsc=self.dsc)
        return PrefmapPluginController(prefmap)

    def _plscr_default(self):
        plscr = PlsrPcrCalcContainer(dsc=self.dsc)
        return PlsrPcrPluginController(plscr)

    def _conjoint_default(self):
        conjoint = ConjointCalcContainer(dsc=self.dsc)
        return ConjointPluginController(conjoint)

    def _ind_diff_default(self):
        ind_diff = IndDiffCalcContainer(dsc=self.dsc)
        return IndDiffPluginController(ind_diff)

    def _toggle_advanced(self):
        self.en_advanced = not self.en_advanced
        print(self.en_advanced)

    # The main view
    traits_ui_view = View(
        Group(Item('dsc',
                   editor=tree_editor,
                   label="Data sets",
                   show_label=False),
              Item('basic_stat',
                   editor=InstanceEditor(view=bs_plugin_view),
                   style='custom',
                   label="Basic stat liking",
                   show_label=False),
              Item('pca',
                   editor=InstanceEditor(view=pca_plugin_view),
                   style='custom',
                   label="PCA",
                   show_label=False),
              Item('prefmap',
                   editor=InstanceEditor(view=prefmap_plugin_view),
                   style='custom',
                   label="Prefmap",
                   show_label=False),
              Item('plscr',
                   editor=InstanceEditor(view=plscr_plugin_view),
                   style='custom',
                   label="PLSR/PCR",
                   show_label=False),
              Item('conjoint',
                   editor=InstanceEditor(view=conjoint_plugin_view),
                   style='custom',
                   label="Conjoint",
                   show_label=False),
              Item('ind_diff',
                   editor=InstanceEditor(view=ind_diff_plugin_view),
                   style='custom',
                   label="Individual differences",
                   show_label=False),
              layout='tabbed'),  # end UI tabs group
        resizable=True,
        width=1000,
        height=600,
        title='ConsumerCheck',
        menubar=MenuBar(
            Menu(
                import_action,
                close_action,
                #                 exit_action,
                name='&File'),
            ## Menu(advanced_action, name='&Settings'),
            Menu(about_action, user_manual_action, name='&Help'),
        ),
        handler=MainViewHandler)
Ejemplo n.º 23
0
class InfoPanel(HasTraits):
    cursor = Property(Tuple)
    cursor_ras = Property(Tuple)
    cursor_tkr = Property(Tuple)
    cursor_intensity = Float

    mouse = Tuple((0.,0.,0.))
    mouse_ras = Tuple((0.,0.,0.))
    mouse_tkr = Tuple((0.,0.,0.))
    mouse_intensity = Float

    cursor_csvlist = List(Float)
    cursor_ras_csvlist = List(Float)
    cursor_tkr_csvlist = List(Float)

    pin_tolerance = Float(7.5)
    currently_showing_list = List(Instance(NullInstanceHolder))
    currently_showing = Instance(NullInstanceHolder)

    add_electrode_button = Button('Make new elec here')
    confirm_movepin_internal_button = Button('Move elec here')
    confirm_movepin_postproc_button = Button('Move postproc')
    track_cursor_button = Button('Track cursor')
    reset_image_button = Button('Center image')

    minimum_contrast = Float( -2000 )
    maximum_contrast = Float( 5000 )

    traits_view = View(
        VGroup(
            Item('currently_showing', 
                editor=InstanceEditor(name='currently_showing_list'),
                style='custom'),
            Spring(),
            HGroup(
            Item('minimum_contrast', editor=TextEditor(enter_set=True,
                auto_set=False, evaluate=float)),
            Item('maximum_contrast', editor=TextEditor(enter_set=True,
                auto_set=False, evaluate=float)),
            ),
            Spring(),
            HGroup(
            Item('add_electrode_button', show_label=False),
            Item('track_cursor_button', show_label=False),
            Item('reset_image_button', show_label=False),
            ),
            HGroup(
            Item('confirm_movepin_internal_button', show_label=False),
            Item('confirm_movepin_postproc_button', show_label=False),
            ),
            Item('pin_tolerance'),
            Spring(),
            Item(name='cursor_csvlist', style='text', label='cursor',
                editor=CSVListEditor(enter_set=True, auto_set=False)),
            Item(name='cursor_ras_csvlist', style='text', label='cursor RAS',
                editor=CSVListEditor(enter_set=True, auto_set=False)),
            Item(name='cursor_tkr_csvlist', style='text', label='cursor tkr',
                editor=CSVListEditor(enter_set=True, auto_set=False)),
            Item(name='cursor_intensity', style='readonly',
                label='cursor intensity'),
            Item(name='mouse', style='readonly', label='mouse'),
            Item(name='mouse_ras', style='readonly', label='mouse RAS'),
            Item(name='mouse_tkr', style='readonly', label='mouse tkr'),
            Item(name='mouse_intensity', style='readonly',
                label='mouse intensity'),
        ),
        height=400, width=400,
        title='ilumbumbargu',
    )

    def _get_cursor(self):
        return tuple(self.cursor_csvlist)
    def _set_cursor(self, newval):
        self.cursor_csvlist = list(newval)
    def _get_cursor_ras(self):
        return tuple(self.cursor_ras_csvlist)
    def _set_cursor_ras(self, newval):
        self.cursor_ras_csvlist = list(newval)
    def _get_cursor_tkr(self):
        return tuple(self.cursor_tkr_csvlist)
    def _set_cursor_tkr(self, newval):
        self.cursor_tkr_csvlist = list(newval)
Ejemplo n.º 24
0
    return 1 if ETSConfig.toolkit == 'qt4' else 6


# The view of the LUT Manager object.
view = View(
    Group(
        Item(name='lut_mode',
             editor=ImageEnumEditor(values=lut_mode_list(),
                                    cols=_number_of_lut_cols(),
                                    path=lut_image_dir)),
        Item(name='file_name', visible_when="lut_mode=='file'"),
        Item(name='number_of_colors'),
        Item(name='reverse_lut'),
        Item(name='lut',
             show_label=False,
             editor=InstanceEditor(label='Edit LUT properties',
                                   id='mayavi.core.lut_manager.edit_lut')),
        Item(name='scalar_bar_representation',
             show_label=False,
             visible_when='scalar_bar_representation is not None',
             editor=InstanceEditor(
                 label='Edit Legend representation',
                 id='mayavi.core.lut_manager.edit_represetation')),
        Item(name='create_lut', show_label=False),
        Group(
            Item(name='show_legend'),
            Group(
                Item(name='number_of_labels'),
                enabled_when='show_scalar_bar==True',
            ),
            Group(
                Item(name='shadow'),
Ejemplo n.º 25
0
 def traits_view(self):
     v = View(
         UItem('plot_panel',
               editor=InstanceEditor(view='summary_view'),
               style='custom'))
     return v
Ejemplo n.º 26
0
            Spring(),
            Item('display_selection',
                 label=_('Show'),
                 editor=EnumEditor(values={
                     'all': _('All'),
                     'remaining': _('Remaining'),
                 })),
        ),
        Item(
            'displayed_items',
            show_label=False,
            style='readonly',
            springy=True,
            editor=ListEditor(
                style='custom',
                editor=InstanceEditor(view=to_do_item_view),
            ),
        ),
    ),
    title=_("To Do List"),
    buttons=[new_item_action],
    menubar=menubar,
    resizable=True,
    width=480,
    height=640,
)

# ----------------------------------------------------------------------------
# TraitsUI Handlers
# ----------------------------------------------------------------------------
Ejemplo n.º 27
0
class DataSourceWizardView(DataSourceWizard):

    #----------------------------------------------------------------------
    # Private traits
    #----------------------------------------------------------------------

    _top_label = Str('Describe your data')

    _info_text = Str('Array size do not match')

    _array_label = Str('Available arrays')

    _data_type_text = Str("What does your data represents?")

    _lines_text = Str("Connect the points with lines")

    _scalar_data_text = Str("Array giving the value of the scalars")

    _optional_scalar_data_text = Str("Associate scalars with the data points")

    _connectivity_text = Str("Array giving the triangles")

    _vector_data_text = Str("Associate vector components")

    _position_text = Property(depends_on="position_type_")

    _position_text_dict = {
        'explicit': 'Coordinnates of the data points:',
        'orthogonal grid': 'Position of the layers along each axis:',
    }

    def _get__position_text(self):
        return self._position_text_dict.get(self.position_type_, "")

    _shown_help_text = Str

    _data_sources_wrappers = Property(depends_on='data_sources')

    def _get__data_sources_wrappers(self):
        return [
            ArrayColumnWrapper(name=name,
                               shape=repr(self.data_sources[name].shape))
            for name in self._data_sources_names
        ]

    # A traits pointing to the object, to play well with traitsUI
    _self = Instance(DataSourceWizard)

    _suitable_traits_view = Property(depends_on="data_type_")

    def _get__suitable_traits_view(self):
        return "_%s_data_view" % self.data_type_

    ui = Any(False)

    _preview_button = Button(label='Preview structure')

    def __preview_button_fired(self):
        if self.ui:
            self.build_data_source()
            self.preview()

    _ok_button = Button(label='OK')

    def __ok_button_fired(self):
        if self.ui:
            self.ui.dispose()
            self.build_data_source()

    _cancel_button = Button(label='Cancel')

    def __cancel_button_fired(self):
        if self.ui:
            self.ui.dispose()

    _is_ok = Bool

    _is_not_ok = Bool

    def _anytrait_changed(self):
        """ Validates if the OK button is enabled.
        """
        if self.ui:
            self._is_ok = self.check_arrays()
            self._is_not_ok = not self._is_ok

    _preview_window = Instance(PreviewWindow, ())

    _info_image = Instance(ImageResource,
                           ImageLibrary.image_resource('@std:alert16', ))

    #----------------------------------------------------------------------
    # TraitsUI views
    #----------------------------------------------------------------------
    _coordinates_group = \
                        HGroup(
                           Item('position_x', label='x',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           Item('position_y', label='y',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           Item('position_z', label='z',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                       )

    _position_group = \
                    Group(
                       Item('position_type'),
                       Group(
                           Item('_position_text', style='readonly',
                                    resizable=False,
                                    show_label=False),
                           _coordinates_group,
                           visible_when='not position_type_=="image data"',
                       ),
                       Group(
                           Item('grid_shape_source_',
                            label='Grid shape',
                            editor=EnumEditor(
                                name='_grid_shape_source_labels',
                                        invalid='_is_not_ok')),
                           HGroup(
                            spring,
                            Item('grid_shape', style='custom',
                                    editor=ArrayEditor(width=-60),
                                    show_label=False),
                           enabled_when='grid_shape_source==""',
                            ),
                           visible_when='position_type_=="image data"',
                       ),
                       label='Position of the data points',
                       show_border=True,
                       show_labels=False,
                   ),

    _connectivity_group = \
                   Group(
                       HGroup(
                         Item('_connectivity_text', style='readonly',
                                resizable=False),
                         spring,
                         Item('connectivity_triangles',
                                editor=EnumEditor(name='_data_sources_names'),
                                show_label=False,
                                ),
                         show_labels=False,
                       ),
                       label='Connectivity information',
                       show_border=True,
                       show_labels=False,
                       enabled_when='position_type_=="explicit"',
                   ),

    _scalar_data_group = \
                   Group(
                       Item('_scalar_data_text', style='readonly',
                           resizable=False,
                           show_label=False),
                       HGroup(
                           spring,
                           Item('scalar_data',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           show_labels=False,
                           ),
                       label='Scalar value',
                       show_border=True,
                       show_labels=False,
                   )

    _optional_scalar_data_group = \
                   Group(
                       HGroup(
                       'has_scalar_data',
                       Item('_optional_scalar_data_text',
                            resizable=False,
                            style='readonly'),
                       show_labels=False,
                       ),
                       Item('_scalar_data_text', style='readonly',
                            resizable=False,
                            enabled_when='has_scalar_data',
                           show_label=False),
                       HGroup(
                           spring,
                           Item('scalar_data',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok'),
                               enabled_when='has_scalar_data'),
                           show_labels=False,
                           ),
                       label='Scalar data',
                       show_border=True,
                       show_labels=False,
                   ),

    _vector_data_group = \
                   VGroup(
                       HGroup(
                           Item('vector_u', label='u',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           Item('vector_v', label='v',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           Item('vector_w', label='w',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                       ),
                       label='Vector data',
                       show_border=True,
                   ),

    _optional_vector_data_group = \
                   VGroup(
                        HGroup(
                            Item('has_vector_data', show_label=False),
                            Item('_vector_data_text', style='readonly',
                                resizable=False,
                                show_label=False),
                        ),
                       HGroup(
                           Item('vector_u', label='u',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           Item('vector_v', label='v',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           Item('vector_w', label='w',
                               editor=EnumEditor(name='_data_sources_names',
                                        invalid='_is_not_ok')),
                           enabled_when='has_vector_data',
                       ),
                       label='Vector data',
                       show_border=True,
                   ),

    _array_view = \
                View(
                    Item('_array_label', editor=TitleEditor(),
                        show_label=False),
                    Group(
                    Item('_data_sources_wrappers',
                      editor=TabularEditor(
                          adapter=ArrayColumnAdapter(),
                      ),
                    ),
                    show_border=True,
                    show_labels=False
                ))

    _questions_view = View(
        Item('_top_label', editor=TitleEditor(), show_label=False),
        HGroup(
            Item('_data_type_text', style='readonly', resizable=False),
            spring,
            'data_type',
            spring,
            show_border=True,
            show_labels=False,
        ),
        HGroup(
            Item(
                '_self',
                style='custom',
                editor=InstanceEditor(view_name='_suitable_traits_view'),
            ),
            Group(
                # FIXME: Giving up on context sensitive help
                # because of lack of time.
                #Group(
                #    Item('_shown_help_text', editor=HTMLEditor(),
                #        width=300,
                #        label='Help',
                #        ),
                #    show_labels=False,
                #    label='Help',
                #),
                #Group(
                Item('_preview_button', enabled_when='_is_ok'),
                Item('_preview_window',
                     style='custom',
                     label='Preview structure'),
                show_labels=False,
                #label='Preview structure',
                #),
                #layout='tabbed',
                #dock='tab',
            ),
            show_labels=False,
            show_border=True,
        ),
    )

    _point_data_view = \
                View(Group(
                   Group(_coordinates_group,
                        label='Position of the data points',
                        show_border=True,
                   ),
                   HGroup(
                       'lines',
                       Item('_lines_text', style='readonly',
                                        resizable=False),
                       label='Lines',
                       show_labels=False,
                       show_border=True,
                   ),
                   _optional_scalar_data_group,
                   _optional_vector_data_group,
                   # XXX: hack to have more vertical space
                   Label('\n'),
                   Label('\n'),
                   Label('\n'),
                ))

    _surface_data_view = \
                View(Group(
                   _position_group,
                   _connectivity_group,
                   _optional_scalar_data_group,
                   _optional_vector_data_group,
                ))

    _vector_data_view = \
                View(Group(
                   _vector_data_group,
                   _position_group,
                   _optional_scalar_data_group,
                ))

    _volumetric_data_view = \
                View(Group(
                   _scalar_data_group,
                   _position_group,
                   _optional_vector_data_group,
                ))

    _wizard_view = View(
        Group(
            HGroup(
                Item(
                    '_self',
                    style='custom',
                    show_label=False,
                    editor=InstanceEditor(view='_array_view'),
                    width=0.17,
                ),
                '_',
                Item(
                    '_self',
                    style='custom',
                    show_label=False,
                    editor=InstanceEditor(view='_questions_view'),
                ),
            ),
            HGroup(
                Item('_info_image',
                     editor=ImageEditor(),
                     visible_when="_is_not_ok"),
                Item('_info_text',
                     style='readonly',
                     resizable=False,
                     visible_when="_is_not_ok"),
                spring,
                '_cancel_button',
                Item('_ok_button', enabled_when='_is_ok'),
                show_labels=False,
            ),
        ),
        title='Import arrays',
        resizable=True,
    )

    #----------------------------------------------------------------------
    # Public interface
    #----------------------------------------------------------------------

    def __init__(self, **traits):
        DataSourceFactory.__init__(self, **traits)
        self._self = self

    def view_wizard(self):
        """ Pops up the view of the wizard, and keeps the reference it to
            be able to close it.
        """
        # FIXME: Workaround for traits bug in enabled_when
        self.position_type_
        self.data_type_
        self._suitable_traits_view
        self.grid_shape_source
        self._is_ok
        self.ui = self.edit_traits(view='_wizard_view')

    def preview(self):
        """ Display a preview of the data structure in the preview
            window.
        """
        self._preview_window.clear()
        self._preview_window.add_source(self.data_source)
        data = lambda name: self.data_sources[name]
        g = Glyph()
        g.glyph.glyph_source.glyph_source = \
                    g.glyph.glyph_source.glyph_list[0]
        g.glyph.scale_mode = 'data_scaling_off'
        if not (self.has_vector_data or self.data_type_ == 'vector'):
            g.glyph.glyph_source.glyph_source.glyph_type = 'cross'
            g.actor.property.representation = 'points'
            g.actor.property.point_size = 3.
        self._preview_window.add_module(g)
        if not self.data_type_ in ('point', 'vector') or self.lines:
            s = Surface()
            s.actor.property.opacity = 0.3
            self._preview_window.add_module(s)
        if not self.data_type_ == 'point':
            self._preview_window.add_filter(ExtractEdges())
            s = Surface()
            s.actor.property.opacity = 0.2
            self._preview_window.add_module(s)
Ejemplo n.º 28
0
class UpdateView(HasTraits):
    piksi_hw_rev = String('piksi_multi')
    is_v2 = Bool(False)

    piksi_stm_vers = String('Waiting for Piksi to send settings...',
                            width=COLUMN_WIDTH)
    newest_stm_vers = String('Downloading Latest Firmware info...')
    piksi_nap_vers = String('Waiting for Piksi to send settings...')
    newest_nap_vers = String('Downloading Latest Firmware info...')
    local_console_vers = String('v' + CONSOLE_VERSION)
    newest_console_vers = String('Downloading Latest Console info...')

    erase_stm = Bool(True)
    erase_en = Bool(True)

    update_stm_firmware = Button(label='Update FW')
    update_nap_firmware = Button(label='Update NAP')
    update_full_firmware = Button(label='Update Piksi STM and NAP Firmware')

    updating = Bool(False)
    update_stm_en = Bool(False)
    update_nap_en = Bool(False)
    update_en = Bool(False)
    serial_upgrade = Bool(False)
    upgrade_steps = String("Firmware upgrade steps:")

    download_firmware = Button(label='Download Latest Firmware')
    download_directory = Directory(
        "  Please choose a directory for downloaded firmware files...")
    download_stm = Button(label='Download', height=HT)
    download_nap = Button(label='Download', height=HT)
    downloading = Bool(False)
    download_fw_en = Bool(True)

    stm_fw = Instance(FirmwareFileDialog)
    nap_fw = Instance(FirmwareFileDialog)

    stream = Instance(OutputStream)

    view = View(
        VGroup(
            Item('piksi_hw_rev',
                 label='Hardware Revision',
                 editor_args={'enabled': False},
                 resizable=True),
            HGroup(
                VGroup(Item('piksi_stm_vers',
                            label='Current',
                            resizable=True,
                            editor_args={'enabled': False}),
                       Item('newest_stm_vers',
                            label='Latest',
                            resizable=True,
                            editor_args={
                                'enabled': False,
                                'readonly_allow_selection': True
                            }),
                       Item('stm_fw',
                            style='custom',
                            show_label=True,
                            label="Local File",
                            enabled_when='download_fw_en',
                            visible_when='serial_upgrade',
                            editor_args={'enabled': False}),
                       HGroup(
                           Item('update_stm_firmware',
                                show_label=False,
                                enabled_when='update_stm_en',
                                visible_when='serial_upgrade'),
                           Item('erase_stm',
                                label='Erase STM flash\n(recommended)',
                                enabled_when='erase_en',
                                show_label=True,
                                visible_when='is_v2')),
                       show_border=True,
                       label="Firmware Version"),
                VGroup(Item('piksi_nap_vers',
                            label='Current',
                            resizable=True,
                            editor_args={'enabled': False}),
                       Item('newest_nap_vers',
                            label='Latest',
                            resizable=True,
                            editor_args={'enabled': False}),
                       Item('nap_fw',
                            style='custom',
                            show_label=True,
                            label="Local File",
                            enabled_when='download_fw_en',
                            editor_args={'enabled': False}),
                       HGroup(
                           Item('update_nap_firmware',
                                show_label=False,
                                enabled_when='update_nap_en',
                                visible_when='serial_upgrade'),
                           Item(width=50, label="                  ")),
                       show_border=True,
                       label="NAP Version",
                       visible_when='is_v2'),
                VGroup(Item('local_console_vers',
                            label='Current',
                            resizable=True,
                            editor_args={'enabled': False}),
                       Item('newest_console_vers',
                            label='Latest',
                            editor_args={'enabled': False}),
                       label="Swift Console Version",
                       show_border=True),
            ), UItem('download_directory', enabled_when='download_fw_en'),
            UItem('download_firmware', enabled_when='download_fw_en'),
            UItem('update_full_firmware',
                  enabled_when='update_en',
                  visible_when='is_v2'),
            VGroup(
                UItem('upgrade_steps',
                      visible_when='not serial_upgrade',
                      style='readonly'),
                Item(
                    'stream',
                    style='custom',
                    editor=InstanceEditor(),
                    show_label=False,
                ),
                show_border=True,
            )))

    def __init__(self,
                 link,
                 download_dir=None,
                 prompt=True,
                 serial_upgrade=False):
        """
        Traits tab with UI for updating Piksi firmware.

        Parameters
        ----------
        link : sbp.client.handler.Handler
          Link for SBP transfer to/from Piksi.
        prompt : bool
          Prompt user to update console/firmware if out of date.
        """
        self.link = link
        self.settings = {}
        self.prompt = prompt
        self.python_console_cmds = {'update': self}
        try:
            self.update_dl = UpdateDownloader()
            if download_dir:
                self.update_dl.set_root_path(download_dir)
        except URLError:
            self.update_dl = None
        self.erase_en = True
        self.stm_fw = FirmwareFileDialog('bin')
        self.stm_fw.on_trait_change(self._manage_enables, 'status')
        self.nap_fw = FirmwareFileDialog('M25')
        self.nap_fw.on_trait_change(self._manage_enables, 'status')
        self.stream = OutputStream()
        self.serial_upgrade = serial_upgrade
        self.last_call_fw_version = None
        if not self.serial_upgrade:
            self._write(
                "1. Insert the USB flash drive provided with your Piki Multi into "
                "your computer.  Select the flash drive root directory as the "
                "firmware download destination using the \"Please "
                "choose a directory for downloaded firmware files\" directory "
                "chooser above.  Press the \"Download Latest Firmware\" button.  "
                "This will download the latest Piksi Multi firmware file onto the "
                "USB flashdrive.\n"
                "2. Eject the drive from your computer and plug it "
                "into the Piksi Multi evaluation board.\n"
                "3. Reset your Piksi Multi and it will upgrade to the version "
                "on the USB flash drive. This should take less than 5 minutes.\n"
                "4. When the upgrade completes you will be prompted to remove the "
                "USB flash drive and reset your Piksi Multi.\n"
                "5. Verify that the firmware version has upgraded via inspection "
                "of the Current Firmware Version box on the Firmware Update Tab "
                "of the Swift Console.\n")

    def _manage_enables(self):
        """ Manages whether traits widgets are enabled in the UI or not. """
        if self.updating or self.downloading:
            self.update_stm_en = False
            self.update_nap_en = False
            self.update_en = False
            self.download_fw_en = False
            self.erase_en = False
        else:
            self.download_fw_en = True
            self.erase_en = True
            if self.stm_fw.ihx is not None or self.stm_fw.blob is not None:
                self.update_stm_en = True
            else:
                self.update_stm_en = False
                self.update_en = False
            if self.nap_fw.ihx is not None:
                self.update_nap_en = True
            else:
                self.update_nap_en = False
                self.update_en = False
            if self.nap_fw.ihx is not None and self.stm_fw.ihx is not None:
                self.update_en = True

    def _download_directory_changed(self):
        if self.update_dl:
            self.update_dl.set_root_path(self.download_directory)

    def _updating_changed(self):
        """ Handles self.updating trait being changed. """
        self._manage_enables()

    def _downloading_changed(self):
        """ Handles self.downloading trait being changed. """
        self._manage_enables()

    def _write(self, text):
        """
        Stream style write function. Allows flashing debugging messages to be
        routed to embedded text console.

        Parameters
        ----------
        text : string
          Text to be written to screen.
        """
        self.stream.write(text)
        self.stream.write('\n')
        self.stream.flush()

    def _update_stm_firmware_fired(self):
        """
        Handle update_stm_firmware button. Starts thread so as not to block the GUI
        thread.
        """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("STM", ))
        self._firmware_update_thread.start()

    def _update_nap_firmware_fired(self):
        """
        Handle update_nap_firmware button. Starts thread so as not to block the GUI
        thread.
        """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("M25", ))
        self._firmware_update_thread.start()

    def _update_full_firmware_fired(self):
        """
        Handle update_full_firmware button. Starts thread so as not to block the GUI
        thread.
        """
        try:
            if self._firmware_update_thread.is_alive():
                return
        except AttributeError:
            pass

        self._firmware_update_thread = Thread(
            target=self.manage_firmware_updates, args=("ALL", ))
        self._firmware_update_thread.start()

    def _download_firmware(self):
        """ Download latest firmware from swiftnav.com. """
        self._write('')

        # Check that we received the index file from the website.
        if self.update_dl is None:
            self._write("Error: Can't download firmware files")
            return

        self.downloading = True
        status = 'Downloading Latest Firmware...'
        self.nap_fw.clear(status)
        self.stm_fw.clear(status)
        self._write(status)

        # Get firmware files from Swift Nav's website, save to disk, and load.
        if 'fw' in self.update_dl.index[self.piksi_hw_rev]:
            try:
                self._write('Downloading Latest Multi firmware')
                filepath = self.update_dl.download_multi_firmware(
                    self.piksi_hw_rev)
                self._write('Saved file to %s' % filepath)
                self.stm_fw.load_bin(filepath)
            except AttributeError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error downloading firmware: index file not downloaded yet"
                )
            except IOError:
                self.nap_fw.clear(
                    "IOError: unable to write to path %s. "
                    "Verify that the path exists and is writable." %
                    self.download_directory)
                self._write("IOError: unable to write to path %s. "
                            "Verify that the path exists and is writable." %
                            self.download_directory)
            except KeyError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error downloading firmware: URL not present in index")
            except URLError:
                self.nap_fw.clear("Error downloading firmware")
                self._write(
                    "Error: Failed to download latest NAP firmware from Swift Navigation's website"
                )
            self.downloading = False
            return

    def _download_firmware_fired(self):
        """
        Handle download_firmware button. Starts thread so as not to block the GUI
        thread.
        """
        try:
            if self._download_firmware_thread.is_alive():
                return
        except AttributeError:
            pass

        self._download_firmware_thread = Thread(target=self._download_firmware)
        self._download_firmware_thread.start()

    def compare_versions(self):
        """
        To be called after latest Piksi firmware info has been received from
        device, to decide if current firmware on Piksi is out of date. Also informs
        user if the firmware was successfully upgraded. Starts a thread so as not
        to block GUI thread.
        """
        try:
            if self._compare_versions_thread.is_alive():
                return
        except AttributeError:
            pass

        self._compare_versions_thread = Thread(target=self._compare_versions)
        self._compare_versions_thread.start()

    def _compare_versions(self):
        """
        Compares version info between received firmware version / current console
        and firmware / console info from website to decide if current firmware or
        console is out of date. Prompt user to update if so. Informs user if
        firmware successfully upgraded.
        """
        # Check that settings received from Piksi contain FW versions.
        try:
            self.piksi_hw_rev = \
                HW_REV_LOOKUP[self.settings['system_info']
                              ['hw_revision'].value]
            self.piksi_stm_vers = \
                self.settings['system_info']['firmware_version'].value
        except KeyError:
            self._write(
                "\nError: Settings received from Piksi don't contain firmware version keys. Please contact Swift Navigation.\n"
            )
            return

        self.is_v2 = self.piksi_hw_rev.startswith('piksi_v2')
        if self.is_v2:
            self.stm_fw.set_flash_type('STM')
            self.serial_upgrade = True
        else:
            self.stm_fw.set_flash_type('bin')

        self._get_latest_version_info()

        # Check that we received the index file from the website.
        if self.update_dl is None:
            self._write(
                "Error: No website index to use to compare versions with local firmware"
            )
            return
        # Get local stm version
        local_stm_version = None
        try:
            local_stm_version = self.settings['system_info'][
                'firmware_version'].value
        except:
            pass
        # Check if console is out of date and notify user if so.
        if self.prompt:
            local_console_version = parse_version(CONSOLE_VERSION)
            remote_console_version = parse_version(self.newest_console_vers)
            self.console_outdated = remote_console_version > local_console_version

            # we want to warn users using v2 regardless of version logic
            if self.console_outdated or self.is_v2:
                if not self.is_v2:
                    console_outdated_prompt = \
                        prompt.CallbackPrompt(
                            title="Swift Console Outdated",
                            actions=[prompt.close_button],
                        )
                    console_outdated_prompt.text = \
                        "Your console is out of date and may be incompatible\n" + \
                        "with current firmware. We highly recommend upgrading to\n" + \
                        "ensure proper behavior.\n\n" + \
                        "Please visit http://support.swiftnav.com to\n" + \
                        "download the latest version.\n\n" + \
                        "Local Console Version :\n\t" + \
                        "v" + CONSOLE_VERSION + \
                        "\nLatest Console Version :\n\t" + \
                        self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"
                else:
                    console_outdated_prompt = \
                        prompt.CallbackPrompt(
                            title="Swift Console Incompatible",
                            actions=[prompt.close_button],
                        )
                    console_outdated_prompt.text = \
                        "Your console is incompatible with your hardware revision.\n" + \
                        "We highly recommend using a compatible console version\n" + \
                        "to ensure proper behavior.\n\n" + \
                        "Please visit http://support.swiftnav.com to\n" + \
                        "download the latest compatible version.\n\n" + \
                        "Current Hardware revision :\n\t" + \
                        self.piksi_hw_rev + \
                        "\nLast supported Console Version: \n\t" + \
                        self.update_dl.index[self.piksi_hw_rev]['console']['version'] + "\n"

                console_outdated_prompt.run()

            # For timing aesthetics between windows popping up.
            sleep(0.5)

            # Check if firmware is out of date and notify user if so.
            remote_stm_version = self.newest_stm_vers

            self.fw_outdated = remote_stm_version > local_stm_version

            if self.fw_outdated:
                fw_update_prompt = \
                    prompt.CallbackPrompt(
                        title='Firmware Update',
                        actions=[prompt.close_button]
                    )

                if 'fw' in self.update_dl.index[self.piksi_hw_rev]:
                    fw_update_prompt.text = \
                        "New Piksi firmware available.\n\n" + \
                        "Please use the Firmware Update tab to update.\n\n" + \
                        "Newest Firmware Version :\n\t%s\n\n" % \
                        self.update_dl.index[self.piksi_hw_rev]['fw']['version']
                else:
                    fw_update_prompt.text = \
                        "New Piksi firmware available.\n\n" + \
                        "Please use the Firmware Update tab to update.\n\n" + \
                        "Newest STM Version :\n\t%s\n\n" % \
                        self.update_dl.index[self.piksi_hw_rev]['stm_fw']['version'] + \
                        "Newest SwiftNAP Version :\n\t%s\n\n" % \
                        self.update_dl.index[self.piksi_hw_rev]['nap_fw']['version']

                fw_update_prompt.run()

        # Check if firmware successfully upgraded and notify user if so.
        if self.last_call_fw_version is not None and \
                self.last_call_fw_version != local_stm_version:
            fw_success_str = "Firmware successfully upgraded from %s to %s." % \
                             (self.last_call_fw_version, local_stm_version)
            print(fw_success_str)
            self._write(fw_success_str)

        # Record firmware version reported each time this callback is called.
        self.last_call_fw_version = local_stm_version

    def _get_latest_version_info(self):
        """ Get latest firmware / console version from website. """
        try:
            self.update_dl = UpdateDownloader()
        except URLError:
            self._write(
                "\nError: Failed to download latest file index from Swift Navigation's website. Please visit our website to check that you're running the latest Piksi firmware and Piksi console.\n"
            )
            return

        # Make sure index contains all keys we are interested in.
        try:
            if 'fw' in self.update_dl.index[self.piksi_hw_rev]:
                self.newest_stm_vers = self.update_dl.index[
                    self.piksi_hw_rev]['fw']['version']
            else:
                self.newest_stm_vers = self.update_dl.index[
                    self.piksi_hw_rev]['stm_fw']['version']
                self.newest_nap_vers = self.update_dl.index[
                    self.piksi_hw_rev]['nap_fw']['version']
            self.newest_console_vers = self.update_dl.index[
                self.piksi_hw_rev]['console']['version']
        except KeyError:
            self._write(
                "\nError: Index downloaded from Swift Navigation's website (%s) doesn't contain all keys. Please contact Swift Navigation.\n"
                % INDEX_URL)
            return

    def manage_stm_firmware_update(self):
        # Erase all of STM's flash (other than bootloader) if box is checked.
        if self.erase_stm:
            text = "Erasing STM"
            self._write(text)
            self.create_flash("STM")
            sectors_to_erase = set(range(self.pk_flash.n_sectors)).difference(
                set(self.pk_flash.restricted_sectors))
            progress_dialog = PulsableProgressDialog(len(sectors_to_erase),
                                                     False)
            progress_dialog.title = text
            GUI.invoke_later(progress_dialog.open)
            erase_count = 0
            for s in sorted(sectors_to_erase):
                progress_dialog.progress(erase_count)
                self._write('Erasing %s sector %d' %
                            (self.pk_flash.flash_type, s))
                self.pk_flash.erase_sector(s)
                erase_count += 1
            self.stop_flash()
            self._write("")
            try:
                progress_dialog.close()
            except AttributeError:
                pass
        # Flash STM.
        text = "Updating STM"
        self._write(text)
        self.create_flash("STM")
        stm_n_ops = self.pk_flash.ihx_n_ops(self.stm_fw.ihx,
                                            erase=not self.erase_stm)
        progress_dialog = PulsableProgressDialog(stm_n_ops, True)
        progress_dialog.title = text
        GUI.invoke_later(progress_dialog.open)
        # Don't erase sectors if we've already done so above.
        self.pk_flash.write_ihx(self.stm_fw.ihx,
                                self.stream,
                                mod_print=0x40,
                                elapsed_ops_cb=progress_dialog.progress,
                                erase=not self.erase_stm)
        self.stop_flash()
        self._write("")
        try:
            progress_dialog.close()
        except AttributeError:
            pass

    def manage_nap_firmware_update(self, check_version=False):
        # Flash NAP if out of date.
        try:
            local_nap_version = parse_version(
                self.settings['system_info']['nap_version'].value)
            remote_nap_version = parse_version(self.newest_nap_vers)
            nap_out_of_date = local_nap_version != remote_nap_version
        except KeyError:
            nap_out_of_date = True
        if nap_out_of_date or not check_version:
            text = "Updating NAP"
            self._write(text)
            self.create_flash("M25")
            nap_n_ops = self.pk_flash.ihx_n_ops(self.nap_fw.ihx)
            progress_dialog = PulsableProgressDialog(nap_n_ops, True)
            progress_dialog.title = text
            GUI.invoke_later(progress_dialog.open)
            self.pk_flash.write_ihx(self.nap_fw.ihx,
                                    self.stream,
                                    mod_print=0x40,
                                    elapsed_ops_cb=progress_dialog.progress)
            self.stop_flash()
            self._write("")
            try:
                progress_dialog.close()
            except AttributeError:
                pass
            return True
        else:
            text = "NAP is already to latest version, not updating!"
            self._write(text)
            self._write("")
            return False

    def manage_multi_firmware_update(self):
        # Set up progress dialog and transfer file to Piksi using SBP FileIO
        progress_dialog = PulsableProgressDialog(len(self.stm_fw.blob))
        progress_dialog.title = "Transferring image file"
        GUI.invoke_later(progress_dialog.open)
        self._write("Transferring image file...")
        try:
            FileIO(self.link).write("upgrade.image_set.bin",
                                    self.stm_fw.blob,
                                    progress_cb=progress_dialog.progress)
        except Exception as e:
            self._write("Failed to transfer image file to Piksi: %s\n" % e)
            progress_dialog.close()
            return
        try:
            progress_dialog.close()
        except AttributeError:
            pass

        # Setup up pulsed progress dialog and commit to flash
        progress_dialog = PulsableProgressDialog(100, True)
        progress_dialog.title = "Committing to flash"
        GUI.invoke_later(progress_dialog.open)
        self._write("Committing file to flash...")

        def log_cb(msg, **kwargs):
            self._write(msg.text)

        self.link.add_callback(log_cb, SBP_MSG_LOG)
        code = shell_command(self.link,
                             "upgrade_tool upgrade.image_set.bin",
                             600,
                             progress_cb=progress_dialog.progress)
        self.link.remove_callback(log_cb, SBP_MSG_LOG)
        progress_dialog.close()

        if code != 0:
            self._write('Failed to perform upgrade (code = %d)' % code)
            if code == -255:
                self._write('Shell command timed out.  Please try again.')
            return
        self._write('Resetting Piksi...')
        self.link(MsgReset(flags=0))

    # Executed in GUI thread, called from Handler.
    def manage_firmware_updates(self, device):
        """
        Update Piksi firmware. Erase entire STM flash (other than bootloader)
        if so directed. Flash NAP only if new firmware is available.
        """
        self.updating = True
        update_nap = False
        self._write('')
        if not self.is_v2:
            self.manage_multi_firmware_update()
            self.updating = False
            return
        elif device == "STM":
            self.manage_stm_firmware_update()
        elif device == "M25":
            update_nap = self.manage_nap_firmware_update()
        else:
            self.manage_stm_firmware_update()
            update_nap = self.manage_nap_firmware_update(check_version=True)

        # Must tell Piksi to jump to application after updating firmware.
        if device == "STM" or update_nap:
            self.link(MsgBootloaderJumpToApp(jump=0))
            self._write("Firmware update finished.")
            self._write("")

        self.updating = False

    def create_flash(self, flash_type):
        """
        Create flash.Flash instance and set Piksi into bootloader mode, prompting
        user to reset if necessary.

        Parameter
        ---------
        flash_type : string
          Either "STM" or "M25".
        """
        # Reset device if the application is running to put into bootloader mode.
        self.link(MsgReset(flags=0))

        self.pk_boot = bootload.Bootloader(self.link)

        self._write("Waiting for bootloader handshake message from Piksi ...")
        reset_prompt = None
        handshake_received = self.pk_boot.handshake(1)

        # Prompt user to reset Piksi if we don't receive the handshake message
        # within a reasonable amount of tiime (firmware might be corrupted).
        while not handshake_received:
            reset_prompt = \
                prompt.CallbackPrompt(
                    title="Please Reset Piksi",
                    actions=[prompt.close_button],
                )

            reset_prompt.text = \
                "You must press the reset button on your Piksi in order\n" + \
                "to update your firmware.\n\n" + \
                "Please press it now.\n\n"

            reset_prompt.run(block=False)

            while not reset_prompt.closed and not handshake_received:
                handshake_received = self.pk_boot.handshake(1)

            reset_prompt.kill()
            reset_prompt.wait()

        self._write("received bootloader handshake message.")
        self._write("Piksi Onboard Bootloader Version: " +
                    self.pk_boot.version)

        self.pk_flash = flash.Flash(self.link, flash_type,
                                    self.pk_boot.sbp_version)

    def stop_flash(self):
        """
        Stop Flash and Bootloader instances (removes callback from SerialLink).
        """
        self.pk_flash.stop()
        self.pk_boot.stop()
Ejemplo n.º 29
0
class GlyphSource(Component):

    # The version of this class.  Used for persistence.
    __version__ = 1

    # Glyph position.  This can be one of ['head', 'tail', 'center'],
    # and indicates the position of the glyph with respect to the
    # input point data.  Please note that this will work correctly
    # only if you do not mess with the source glyph's basic size.  For
    # example if you use a ConeSource and set its height != 1, then the
    # 'head' and 'tail' options will not work correctly.
    glyph_position = PrefixList(
        ['head', 'tail', 'center'],
        default_value='center',
        desc='position of glyph w.r.t. data point')

    # The Source to use for the glyph.  This is chosen from
    # `self._glyph_list` or `self.glyph_dict`.
    glyph_source = Instance(tvtk.Object, allow_none=False, record=True)

    # A dict of glyphs to use.
    glyph_dict = Dict(desc='the glyph sources to select from',
                      record=False)

    # A list of predefined glyph sources that can be used.
    glyph_list = Property(List(tvtk.Object), record=False)

    ########################################
    # Private traits.

    # The transformation to use to place glyph appropriately.
    _trfm = Instance(tvtk.TransformFilter, args=())

    # Used for optimization.
    _updating = Bool(False)

    ########################################
    # View related traits.

    view = View(
        Group(
            Group(Item(name='glyph_position')),
            Group(
                Item(
                    name='glyph_source',
                    style='custom',
                    resizable=True,
                    editor=InstanceEditor(name='glyph_list'),
                ),
                label='Glyph Source',
                show_labels=False)),
        resizable=True
    )

    ######################################################################
    # `Base` interface
    ######################################################################
    def __get_pure_state__(self):
        d = super(GlyphSource, self).__get_pure_state__()
        for attr in ('_updating', 'glyph_list'):
            d.pop(attr, None)
        return d

    def __set_pure_state__(self, state):
        if 'glyph_dict' in state:
            # Set their state.
            set_state(self, state, first=['glyph_dict'], ignore=['*'])
            ignore = ['glyph_dict']
        else:
            # Set the dict state using the persisted list.
            gd = self.glyph_dict
            gl = self.glyph_list
            handle_children_state(gl, state.glyph_list)
            for g, gs in zip(gl, state.glyph_list):
                name = camel2enthought(g.__class__.__name__)
                if name not in gd:
                    gd[name] = g
                # Set the glyph source's state.
                set_state(g, gs)
            ignore = ['glyph_list']
        g_name = state.glyph_source.__metadata__['class_name']
        name = camel2enthought(g_name)
        # Set the correct glyph_source.
        self.glyph_source = self.glyph_dict[name]
        set_state(self, state, ignore=ignore)

    ######################################################################
    # `Component` interface
    ######################################################################
    def setup_pipeline(self):
        """Override this method so that it *creates* the tvtk
        pipeline.

        This method is invoked when the object is initialized via
        `__init__`.  Note that at the time this method is called, the
        tvtk data pipeline will *not* yet be setup.  So upstream data
        will not be available.  The idea is that you simply create the
        basic objects and setup those parts of the pipeline not
        dependent on upstream sources and filters.  You should also
        set the `actors` attribute up at this point.
        """

        self._trfm.transform = tvtk.Transform()
        # Setup the glyphs.
        self.glyph_source = self.glyph_dict['glyph_source2d']

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when any of the inputs
        sends a `pipeline_changed` event.
        """
        self._glyph_position_changed(self.glyph_position)
        self.pipeline_changed = True

    def update_data(self):
        """Override this method so that it flushes the vtk pipeline if
        that is necessary.

        This method is invoked (automatically) when any of the inputs
        sends a `data_changed` event.
        """
        self.data_changed = True

    def render(self):
        if not self._updating:
            super(GlyphSource, self).render()

    ######################################################################
    # Non-public methods.
    ######################################################################
    def _glyph_source_changed(self, value):
        if self._updating:
            return

        gd = self.glyph_dict
        value_cls = camel2enthought(value.__class__.__name__)
        if value not in gd.values():
            gd[value_cls] = value

        # Now change the glyph's source trait.
        self._updating = True
        recorder = self.recorder
        if recorder is not None:
            name = recorder.get_script_id(self)
            lhs = '%s.glyph_source' % name
            rhs = '%s.glyph_dict[%r]' % (name, value_cls)
            recorder.record('%s = %s' % (lhs, rhs))

        name = value.__class__.__name__
        if name == 'GlyphSource2D':
            self.outputs = [value]
        else:
            self.configure_input(self._trfm, value)
            self.outputs = [self._trfm]
        value.on_trait_change(self.render)
        self._updating = False

        # Now update the glyph position since the transformation might
        # be different.
        self._glyph_position_changed(self.glyph_position)

    def _glyph_position_changed(self, value):
        if self._updating:
            return

        self._updating = True
        tr = self._trfm.transform
        tr.identity()

        g = self.glyph_source
        name = g.__class__.__name__
        # Compute transformation factor
        if name == 'CubeSource':
            tr_factor = g.x_length/2.0
        elif name == 'CylinderSource':
            tr_factor = -g.height/2.0
        elif name == 'ConeSource':
            tr_factor = g.height/2.0
        elif name == 'SphereSource':
            tr_factor = g.radius
        else:
            tr_factor = 1.
        # Translate the glyph
        if value == 'tail':
            if name == 'GlyphSource2D':
                g.center = 0.5, 0.0, 0.0
            elif name == 'ArrowSource':
                pass
            elif name == 'CylinderSource':
                g.center = 0, tr_factor, 0.0
            elif hasattr(g, 'center'):
                g.center = tr_factor, 0.0, 0.0
        elif value == 'head':
            if name == 'GlyphSource2D':
                g.center = -0.5, 0.0, 0.0
            elif name == 'ArrowSource':
                tr.translate(-1, 0, 0)
            elif name == 'CylinderSource':
                g.center = 0, -tr_factor, 0.0
            else:
                g.center = -tr_factor, 0.0, 0.0
        else:
            if name == 'ArrowSource':
                tr.translate(-0.5, 0, 0)
            elif name != 'Axes':
                g.center = 0.0, 0.0, 0.0

        if name == 'CylinderSource':
            tr.rotate_z(90)

        self._updating = False
        self.render()

    def _get_glyph_list(self):
        # Return the glyph list as per the original order in earlier
        # implementation.
        order = ['glyph_source2d', 'arrow_source', 'cone_source',
                 'cylinder_source', 'sphere_source', 'cube_source',
                 'axes']
        gd = self.glyph_dict
        for key in gd:
            if key not in order:
                order.append(key)
        return [gd[key] for key in order]

    def _glyph_dict_default(self):
        g = {'glyph_source2d': tvtk.GlyphSource2D(glyph_type='arrow',
                                                  filled=False),
             'arrow_source': tvtk.ArrowSource(),
             'cone_source': tvtk.ConeSource(height=1.0, radius=0.2,
                                            resolution=15),
             'cylinder_source': tvtk.CylinderSource(height=1.0, radius=0.15,
                                                    resolution=10),
             'sphere_source': tvtk.SphereSource(),
             'cube_source': tvtk.CubeSource(),
             'axes': tvtk.Axes(symmetric=1)}
        return g
Ejemplo n.º 30
0
class ImplicitPlane(Component):
    # The version of this class.  Used for persistence.
    __version__ = 0

    # The widget that controls the plane.
    widget = Instance(tvtk.ImplicitPlaneWidget, args=(),
                      kw={'key_press_activation': False,
                          'place_factor':1.2,
                          'draw_plane':False,
                          'outline_translation':False},
                      record=True)

    # The plane that the widget controls.  Do not change the
    # attributes of the plane, do it via the widget.
    plane = Instance(tvtk.Plane, args=(),
                     kw={'origin':(0.0, 0.0, 0.0),
                         'normal':(0,0,1)},
                     record=True)

    # Convenience property for the normal delegated to the widget.
    normal = Property

    # Convenience property for the origin delegated to the widget.
    origin = Property

    ########################################
    # Private traits

    _first = Bool(True)
    _busy = Bool(False)
    _bounds = Any

    ########################################
    # View related traits.

    if VTK_VER[:3] in ['4.2', '4.4']:
        _widget_group = Group(Item(name='enabled'),
                              Item(name='normal_to_x_axis'),
                              Item(name='normal_to_y_axis'),
                              Item(name='normal_to_z_axis'),
                              Item(name='outline_translation'),
                              Item(name='tubing'),
                              Item(name='draw_plane'),
                              Item(name='normal'),
                              Item(name='origin')
                              )
    else:
        _widget_group = Group(Item(name='enabled'),
                              Item(name='normal_to_x_axis'),
                              Item(name='normal_to_y_axis'),
                              Item(name='normal_to_z_axis'),
                              Item(name='outline_translation'),
                              Item(name='scale_enabled'),
                              Item(name='tubing'),
                              Item(name='draw_plane'),
                              Item(name='normal'),
                              Item(name='origin')
                              )

    view = View(Group(Item(name='widget', style='custom',
                           editor=InstanceEditor(view=View(_widget_group))),
                      show_labels=False)
                )

    ######################################################################
    # `Component` interface
    ######################################################################
    def setup_pipeline(self):
        """Override this method so that it *creates* its tvtk
        pipeline.

        This method is invoked when the object is initialized via
        `__init__`.  Note that at the time this method is called, the
        tvtk data pipeline will *not* yet be setup.  So upstream data
        will not be available.  The idea is that you simply create the
        basic objects and setup those parts of the pipeline not
        dependent on upstream sources and filters.
        """
        # Setup our widgets and hook up all handlers.
        self.widgets = [self.widget]
        self._connect()

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when the input fires a
        `pipeline_changed` event.
        """
        if len(self.inputs) == 0 or len(self.inputs[0].outputs) == 0:
            return
        inp = self.inputs[0].outputs[0]
        w = self.widget
        self.configure_input(w, inp)
        if self._first:
            dsh = DataSetHelper(self.inputs[0].outputs[0])
            self._bounds = dsh.get_bounds()
            w.place_widget(*self._bounds)
            self.origin = dsh.get_center()
            self._first = False
        else:
            n = self.normal
            # A hack to update the widget when data changes upstream.
            # This is perhaps a VTK bug, not sure.
            self.normal = n[0], n[1], n[2] + 0.001
            self.normal = n

        # Just pass the inputs back out.  This may trigger a pipeline
        # changed downstream if it does not then fire a data_changed.
        if self.outputs != [inp]:
            self.outputs = [inp]
        else:
            self.data_changed = True

    def update_data(self):
        """Override this method to do what is necessary when upstream
        data changes.

        This method is invoked (automatically) when any of the inputs
        sends a `data_changed` event.
        """
        self.data_changed = True

    def update_plane(self):
        """Convenience method to update the plane once the widget is
        changed.
        """
        self.widget.get_plane(self.plane)
        self.update_data()

    ######################################################################
    # Non-public interface.
    ######################################################################
    def _get_normal(self):
        return self.widget.normal

    def _set_normal(self, value):
        w = self.widget
        old = w.normal
        w.normal = value
        self.trait_property_changed('normal', old, value)
        self.update_plane()

    def _get_origin(self):
        return self.widget.origin
    def _set_origin(self, value):
        # Ugly, but needed.
        w = tvtk.to_vtk(self.widget)
        old = w.GetOrigin()
        w.SetOrigin(list(value))
        self.trait_property_changed('origin', old, value)
        self.update_plane()

    def _on_interaction_event(self, obj, event):
        if not self._busy:
            self._busy = True
            self.update_plane()
            self._busy = False

    def _on_normal_set(self):
        w = self.widget
        w.place_widget(*self._bounds)
        w.update_traits()

    def _connect(self):
        """Wires up all the event handlers."""
        w = self.widget
        w.add_observer('InteractionEvent',
                       self._on_interaction_event)
        w.on_trait_change(self._on_normal_set, 'normal_to_x_axis')
        w.on_trait_change(self._on_normal_set, 'normal_to_y_axis')
        w.on_trait_change(self._on_normal_set, 'normal_to_z_axis')
        w.on_trait_change(self._on_interaction_event)

        for obj in (self.plane, w):
            obj.on_trait_change(self.render)