示例#1
0
class ApplicationWindow(MApplicationWindow, Window):
    """ The toolkit specific implementation of an ApplicationWindow.  See the
    IApplicationWindow interface for the API documentation.
    """

    implements(IApplicationWindow)

    #### 'IApplicationWindow' interface #######################################

    icon = Instance(ImageResource)

    menu_bar_manager = Instance(MenuBarManager)

    status_bar_manager = Instance(StatusBarManager)

    tool_bar_manager = Instance(ToolBarManager)

    # If the underlying toolkit supports multiple toolbars then you can use
    # this list instead.
    tool_bar_managers = List(ToolBarManager)

    #### 'IWindow' interface ##################################################

    # fixme: We can't set the default value of the actual 'size' trait here as
    # in the toolkit-specific event handlers for window size and position
    # changes, we set the value of the shadow '_size' trait. The problem is
    # that by doing that traits never knows that the trait has been set and
    # hence always returns the default value! Using a trait initializer method
    # seems to work however (e.g. 'def _size_default'). Hmmmm....
    ##     size = (800, 600)

    title = Unicode("Pyface")

    ###########################################################################
    # Protected 'IApplicationWindow' interface.
    ###########################################################################

    def _create_contents(self, parent):
        panel = wx.Panel(parent, -1)
        panel.SetSize((500, 400))
        panel.SetBackgroundColour('blue')

        return panel

    def _create_menu_bar(self, parent):
        if self.menu_bar_manager is not None:
            menu_bar = self.menu_bar_manager.create_menu_bar(parent)
            self.control.SetMenuBar(menu_bar)

    def _create_status_bar(self, parent):
        if self.status_bar_manager is not None:
            status_bar = self.status_bar_manager.create_status_bar(parent)
            self.control.SetStatusBar(status_bar)

        return

    def _create_tool_bar(self, parent):
        tool_bar_managers = self._get_tool_bar_managers()
        if len(tool_bar_managers) > 0:
            if AUI:
                for tool_bar_manager in reversed(tool_bar_managers):
                    tool_bar = tool_bar_manager.create_tool_bar(parent)
                    self._add_toolbar_to_aui_manager(tool_bar,
                                                     tool_bar_manager.name)
            else:
                tool_bar = tool_bar_managers[0].create_tool_bar(parent)
                self.control.SetToolBar(tool_bar)

    def _set_window_icon(self):
        if self.icon is None:
            icon = ImageResource('application.ico')
        else:
            icon = self.icon

        if self.control is not None:
            self.control.SetIcon(icon.create_icon())

        return

    ###########################################################################
    # 'Window' interface.
    ###########################################################################

    def _size_default(self):
        """ Trait initialiser. """

        return (800, 600)

    ###########################################################################
    # Protected 'IWidget' interface.
    ###########################################################################

    def _create(self):

        if AUI:
            # fixme: We have to capture the AUI manager as an attribute,
            # otherwise it gets garbage collected and we get a core dump...
            # Ahh, the sweet smell of open-source ;^()
            self._aui_manager = wx.aui.AuiManager()

        super(ApplicationWindow, self)._create()

        if AUI:
            body = self._create_body(self.control)
            contents = self._create_contents(body)
            body.GetSizer().Add(contents, 1, wx.EXPAND)
            body.Fit()

        else:
            contents = self._create_contents(self.control)

        self._create_trim_widgets(self.control)

        if AUI:
            # Updating the AUI manager actually commits all of the pane's added
            # to it (this allows batch updates).
            self._aui_manager.Update()

        return

    def _create_control(self, parent):

        style = wx.DEFAULT_FRAME_STYLE \
                | wx.FRAME_NO_WINDOW_MENU \
                | wx.CLIP_CHILDREN

        control = wx.Frame(parent,
                           -1,
                           self.title,
                           style=style,
                           size=self.size,
                           pos=self.position)

        control.SetBackgroundColour(SystemMetrics().dialog_background_color)

        if AUI:
            # Let the AUI manager look after the frame.
            self._aui_manager.SetManagedWindow(control)

        return control

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _add_toolbar_to_aui_manager(self, tool_bar, name='Tool Bar'):
        """ Add a toolbar to the AUI manager. """

        info = wx.aui.AuiPaneInfo()
        info.Caption(name)
        info.LeftDockable(False)
        info.Name(name)
        info.RightDockable(False)
        info.ToolbarPane()
        info.Top()

        self._aui_manager.AddPane(tool_bar, info)

        return

    def _create_body(self, parent):
        """ Create the body of the frame. """

        panel = wx.Panel(parent, -1)
        sizer = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(sizer)

        info = wx.aui.AuiPaneInfo()
        info.Caption('Body')
        info.Dockable(False)
        info.Floatable(False)
        info.Name('Body')
        info.CentrePane()

        self._aui_manager.AddPane(panel, info)

        return panel

    def _get_tool_bar_managers(self):
        """ Return all tool bar managers specified for the window. """

        # fixme: V3 remove the old-style single toolbar option!
        if self.tool_bar_manager is not None:
            tool_bar_managers = [self.tool_bar_manager]

        else:
            tool_bar_managers = self.tool_bar_managers

        return tool_bar_managers

    def _wx_enable_tool_bar(self, tool_bar, enabled):
        """ Enable/Disablea tool bar. """

        if AUI:
            # AUI toolbars cannot be enabled/disabled.
            pass

        else:
            tool_bar.Enable(enabled)

        return

    def _wx_show_tool_bar(self, tool_bar, visible):
        """ Hide/Show a tool bar. """

        if AUI:
            pane = self._aui_manager.GetPane(tool_bar.tool_bar_manager.name)

            if visible:
                pane.Show()

            else:
                pane.Hide()

            self._aui_manager.Update()

        else:
            tool_bar.Show(visible)

        return

    #### Trait change handlers ################################################
    def _icon_changed(self):
        self._set_window_icon()
class EggboxPESDataSourceModel(BaseDataSourceModel):
    """ This model stores all the data required to compute the
    potential. All randomness must be contained in the model, not at
    the instance-level. Changing any of the parameters used to generate
    the potential will generate an entirely new random potential.

    """

    # traits controlled by user
    dimension = PositiveInt(
        2,
        label='Dimensionality',
        changes_slots=True
    )
    cuba_design_space_type = Unicode(
        changes_slots=True,
        label='Parameter space type/units'
    )
    cuba_potential_type = Unicode(
        changes_slots=True,
        label='Potential type'
    )
    num_cells = PositiveInt(
        5,
        label='Number of cells',
        desc='Number of lattice points in each direction'
    )
    sigma_star = Float(
        0.1,
        label='σ*',
        desc='Variance of basin depths: σ*~0 will lead to identical basins '
             'σ*~1 normally lead to a few basins dominating'
    )
    locally_optimize = Bool(
        True,
        label='Locally optimize trials?',
        desc='Whether or not to locally optimize each '
             'trial and return the local minima'
    )

    # traits set by calculation
    basin_depths = List()
    basin_positions = List()

    # these lists can be useful for debugging and plotting, they contain
    # the trial values and results at each step of the MCO (see
    # `scripts/`)
    trials = List()
    results = List()

    traits_view = View([Item('locally_optimize'),
                        Item('sigma_star'),
                        Item('num_cells'),
                        Item('dimension'),
                        Item('cuba_design_space_type'),
                        Item('cuba_potential_type')])

    def __init__(self, *args, **kwargs):
        super(EggboxPESDataSourceModel, self).__init__(*args, **kwargs)
        self._set_basin_positions()
        self._randomise_model_data()

    @on_trait_change('sigma_star,basin_positions')
    def _randomise_model_data(self):
        """ Assign random depths to defined basins, with variance
        controlled by self.sigma_star.

        """
        self.basin_depths = ((self.sigma_star *
                              np.random.rand(len(self.basin_positions)))
                             .tolist())

    @on_trait_change('num_cells,dimension')
    def _set_basin_positions(self):
        """ Construct the array of basin positions for the given lattice.
        The square lattice is the only one implemented in this example.

        """
        self._set_basin_positions_square_lattice()

    def _set_basin_positions_square_lattice(self):
        """ Set the basin positions to a square lattice from 0 -> 1. """
        grids = self.dimension * [np.linspace(0, 1,
                                              num=self.num_cells,
                                              endpoint=False)]

        self.basin_positions = ((np.asarray(np.meshgrid(*grids))
                                 .reshape(self.dimension, -1).T)
                                .tolist())
class ParameterTemplate(BaseTemplate):
    """BaseTemplate subclass to generate MCO Parameter options for
    SurfactantContributedUI"""

    # --------------------
    #  Regular Attributes
    # --------------------

    #: String representing MCOParameter subclass
    parameter_type = Enum('Fixed', 'Ranged', 'Listed')

    #: Name of Parameter
    name = Unicode()

    #: CUBA type of Parameter
    type = Unicode('CONCENTRATION')

    #: MCOParameter level trait
    value = Float(1.0)

    #: RangedMCOParameter lower_bound trait
    lower_bound = Float(0.5)

    #: RangedMCOParameter upper_bound trait
    upper_bound = Float(5.0)

    #: RangedMCOParameter n_samples trait
    n_samples = Int(10)

    #: ListedMCOParameter levels trait
    levels = ListFloat([0.5, 1.0, 3.0])

    # --------------------
    #      Properties
    # --------------------

    #: Factory ID for Workflow
    id = Property(Unicode, depends_on='plugin_id,type')

    # --------------------
    #        View
    # --------------------

    traits_view = View(
        Item('parameter_type'),
        Item("value", visible_when="parameter_type=='Fixed'"),
        Item("lower_bound", visible_when="parameter_type=='Ranged'"),
        Item("upper_bound", visible_when="parameter_type=='Ranged'"),
        Item("n_samples", visible_when="parameter_type=='Ranged'"),
        Item("levels",
             editor=ListEditor(style='simple'),
             visible_when="parameter_type=='Listed'"))

    # --------------------
    #      Listeners
    # --------------------

    def _get_id(self):
        return '.'.join(
            [self.plugin_id, 'parameter',
             self.parameter_type.lower()])

    # --------------------
    #    Public Methods
    # --------------------

    def create_template(self):
        template = {
            "id": self.id,
            "model_data": {
                "name": f"{self.name}_conc",
                "type": self.type
            }
        }

        if self.parameter_type == 'Fixed':
            template['model_data']["value"] = self.value
        elif self.parameter_type == 'Ranged':
            template['model_data']["lower_bound"] = self.lower_bound
            template['model_data']["upper_bound"] = self.upper_bound
            template['model_data']["n_samples"] = self.n_samples
        elif self.parameter_type == 'Listed':
            template['model_data']["levels"] = self.levels

        return template
示例#4
0
class GromacsDatabase(HasStrictTraits):
    """Class that can perform query and parse a JSON file containing
    serialised representations of Ingredient objects"""

    #: File path for JSON containing database of Gromacs objects
    file_path = Unicode()

    #: Reader for Gromacs .itp molecule files
    _reader = GromacsMoleculeReader()

    #: Cache for storing loaded fragments
    _fragment_cache = Dict()

    #: Parsed data from JSON database
    _data = Property(Dict, depends_on='file_path')

    def _file_path_default(self):
        return get_file("chemical_database.json")

    def _get__data(self):
        """Update _data attribute if new file path defined"""
        return self._load_data(self.file_path)

    def _load_data(self, file_path):
        """Load JSON file containing chemical Ingredient data"""
        with open(file_path, 'r') as infile:
            data = json.load(infile)
        return data

    def _parse_fragment_data(self, data):
        """Parse a dictionary containing fragment data
        to provide absolute file paths"""

        for fragment in data:
            fragment["topology"] = get_file(fragment['topology'])
            fragment["coordinate"] = get_file(fragment['coordinate'])

    def get_fragment(self, topology, symbol, **kwargs):
        """Retrieve required GromacsFragment from topology
        file and update attributes

        Parameters
        ----------
        topology: str
            File path of Gromacs '.itp' file containing
            fragment data
        symbol: str
            Symbol corresponging to target fragment in
            topology file

        Returns
        -------
        fragment: GromacsFragment
            Container class with fragment information

        Raises
        ------
        MissingFragmentException: if no GromacsFragment with
        matching `symbol` atribute is loaded from topology
        file"""

        fragments = self._reader.read(topology)

        # Identify fragment loaded from Gromacs molecule
        # file and update attributes with those from database
        for fragment in fragments:
            if fragment.symbol == symbol:
                # Update fragment in file with additional
                # attribute data
                for key, value in kwargs.items():
                    setattr(fragment, key, value)
                return fragment

        raise MissingFragmentException

    def get_ingredient(self, name):
        """Return an Ingredient instance from data that is obtained
        using the name argument as a query"""

        ingredient_data = self._data[name]
        fragment_data = ingredient_data.pop("fragments", None)
        self._parse_fragment_data(fragment_data)

        ingredient_fragments = []
        for kwargs in fragment_data:
            try:
                fragment = self._fragment_cache[kwargs['symbol']]
            except KeyError:
                fragment = self.get_fragment(**kwargs)
                self._fragment_cache[fragment.symbol] = fragment
            ingredient_fragments.append(fragment)

        ingredient = Ingredient(name=name,
                                fragments=ingredient_fragments,
                                **ingredient_data)

        return ingredient

    def get_role(self, role):
        """Return a list of Ingredient instance from data that have
        a matching role attribute as input argument"""

        ingredients = []
        for key, value in self._data.items():
            if value['role'] == role:
                ingredients.append(self.get_ingredient(key))

        return ingredients
示例#5
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(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 = Unicode()
    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)

    local_file_for_fileio = String()
    choose_local_file = Button(label='...', padding=-1)
    destination_path_for_fileio = '/persistent/licenses/smoothpose_license.json'
    send_file_to_device = Button()

    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(
                    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)
            ),
            HGroup(
                VGroup(
                    Item(
                        'stream',
                        style='custom',
                        editor=InstanceEditor(),
                        show_label=False, ),
                    show_border=True,
                    label="Firmware Upgrade Status"),
            ),
            HGroup(
                Item('local_file_for_fileio', label="Local File"),
                Item('choose_local_file', show_label=False),
                Item('destination_path_for_fileio', label="Destination Path"),
                Item("send_file_to_device", show_label=False),
                show_border=True,
                label="File IO and product feature unlock tool"
            )
        )
    )

    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.nw_chk = NetworkCheck(self._write, self.set_download_fw_en)
        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 set_download_fw_en(self, value):
        self.download_fw_en = value

    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 _send_file(self):
        blob = open(self.local_file_for_fileio, 'rb').read()
        self.blob_size = float(len(blob))
        self.pcent_complete = 0
        FileIO(self.link).write(bytes(self.destination_path_for_fileio, 'ascii'),
                                blob,
                                progress_cb=self.file_transfer_progress_cb)

    def _send_file_to_device_fired(self):
        self._write("Initiating file transfer of {} to location {}".format(
            self.local_file_for_fileio, self.destination_path_for_fileio))
        if not os.path.isfile(self.local_file_for_fileio):
            self._write("Error with path: {} is not a file".format(self.local_file_for_fileio))
            return
        else:
            self._send_file_thread = Thread(target=self._send_file)
            self._send_file_thread.start()

    def _choose_local_file_fired(self):
        dialog = FileDialog(
            label='Choose local_file',
            action='open')
        dialog.open()
        if dialog.return_code == OK:
            self.local_file_for_fileio = dialog.path
        else:
            self._write('Error while selecting local file.')

    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.
        """
        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):
        if not self.nw_chk.network_is_reachable():
            return

        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('.*-(v[0-9]*\.[0-9]*\.[0-9]*.*).bin$', self.stm_fw.status)
        intended_version = parse_version(str(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. But always allow development version
        if (current_fw_version.isdev is False) and (intended_version.isdev is False) and (current_fw_version < parse_version("v2.0.0")) and (intended_version > parse_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. """
        if not self.nw_chk.network_is_reachable():
            return

        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(
                "\nWarning: Settings received from Piksi do not contain firmware version information. Unable to determine software update status.\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(
                "\nWarning: Unable to fetch firmware release index from Swift to determine update status.\n"
            )
            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" + \
                        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. """
        self.update_dl = None

        if not self.nw_chk.network_is_reachable():
            return

        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"
            )
            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, offset, repeater):
        new_pcent = float(offset) / 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:
            msg_text = msg.text.decode('utf8')
            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(
                b"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,
            b"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
示例#6
0
class Wizard(MWizard, Dialog):
    """ The base class for all pyface wizards.

    See the IWizard interface for the API documentation.

    """

    #### 'IWizard' interface ##################################################

    pages = Property(List(IWizardPage))

    controller = Instance(IWizardController)

    show_cancel = Bool(True)

    #### 'IWindow' interface ##################################################

    title = Unicode('Wizard')

    ###########################################################################
    # 'IWizard' interface.
    ###########################################################################

    # Override MWizard implementation to do nothing. We still call these methods
    # because it expected by IWizard, and users may wish to hook in custom code
    # before changing a page.

    def next(self):
        pass

    def previous(self):
        pass

    ###########################################################################
    # Protected 'IDialog' interface.
    ###########################################################################

    def _create_contents(self, parent):
        pass

    ###########################################################################
    # Protected 'IWidget' interface.
    ###########################################################################

    def _create_control(self, parent):
        control = _Wizard(parent, self)
        control.setOptions(QtGui.QWizard.NoDefaultButton
                           | QtGui.QWizard.NoBackButtonOnStartPage)
        control.setModal(self.style == 'modal')
        control.setWindowTitle(self.title)

        # Necessary for 'nonmodal'. See Dialog for more info.
        if self.style == 'nonmodal':
            QtCore.QObject.connect(control, QtCore.SIGNAL('finished(int)'),
                                   self._finished_fired)

        if self.size != (-1, -1):
            size = QtCore.QSize(*self.size)
            control.setMinimumSize(size)
            control.resize(size)

        if not self.show_cancel:
            control.setOption(QtGui.QWizard.NoCancelButton)

        if self.help_id:
            control.setOption(QtGui.QWizard.HaveHelpButton)
            QtCore.QObject.connect(control, QtCore.SIGNAL('helpRequested()'),
                                   self._help_requested)

        # Add the initial pages.
        for page in self.pages:
            page.pyface_wizard = self
            control.addWizardPage(page)

        # Set the start page.
        control.setStartWizardPage()

        return control

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _help_requested(self):
        """ Called when the 'Help' button is pressed. """

        # FIXME: Hook into a help system.
        print "Show help for", self.help_id

    #### Trait handlers #######################################################

    def _get_pages(self):
        """ The pages getter. """

        return self.controller.pages

    def _set_pages(self, pages):
        """ The pages setter. """

        # Remove pages from the old list that appear in the new list.  The old
        # list will now contain pages that are no longer in the wizard.
        old_pages = self.pages
        new_pages = []

        for page in pages:
            try:
                old_pages.remove(page)
            except ValueError:
                new_pages.append(page)

        # Dispose of the old pages.
        for page in old_pages:
            page.dispose_page()

        # If we have created the control then we need to add the new pages,
        # otherwise we leave it until the control is created.
        if self.control:
            for page in new_pages:
                self.control.addWizardPage(page)

        self.controller.pages = pages

    def _controller_default(self):
        """ Provide a default controller. """

        from pyface.wizard.wizard_controller import WizardController

        return WizardController()
示例#7
0
class AskName(HasTraits):
    name = Unicode("")
    view = View(Item("name", label="名称"),
                kind="modal",
                buttons=OKCancelButtons)
示例#8
0
class PCPlotData(ArrayPlotData):
    """Container for Principal Component scatterplot type data set.

    This container will be able to hold several sets of PC type data sets:
     * The actual matrix with PC1 to PCn
     * A list of PCDataSet objects that holds metadata for each PC matrix
    """

    # Metadata for each PC set
    plot_data = List(PCDataSet)
    group_names = List([''])
    plot_group = Unicode('')
    coloring_factor = Instance(Factor)
    # Number of PC in the data sets
    # Lowest number if we have severals sets
    n_pc = Long()
    # The PC for X the axis
    x_no = Int()
    # The PC for the Y axis
    y_no = Int()

    def add_PC_set(self, pc_ds, expl_vars, factor=None):
        """Add a PC data set with metadata"""
        set_n = len(self.plot_data)

        if set_n == 0:
            self.n_pc = pc_ds.n_vars
        else:
            self.n_pc = min(self.n_pc, pc_ds.n_vars)

        values = pc_ds.values.transpose()
        for j, row in enumerate(values):
            dict_name = 's{}pc{}'.format(set_n + 1, (j + 1))
            self.arrays[dict_name] = row

        # if factor is not None:
        #     self.coloring_factor = factor

        if len(pc_ds.subs) > 0:
            # FIXME: replaced by update_color_level_data()
            for gn in pc_ds.get_subset_groups():
                self.group_names.append(gn)
                subsets = pc_ds.get_subsets(gn)
                for ss in subsets:
                    sarray = pc_ds.get_subset_rows(ss)
                    values = sarray.values.transpose()
                    for j, row in enumerate(values):
                        dict_name = 's{}pc{}g{}c{}'.format(
                            set_n + 1, (j + 1), gn, ss.id)
                        self.arrays[dict_name] = row
                    pass
                pass
            pass

        labels = pc_ds.obj_n
        color = pc_ds.style.fg_color

        pcds = PCDataSet()
        if labels is not None:
            pcds.labels = labels
        if color is not None:
            pcds.color = color
        if expl_vars is not None:
            pcds.expl_vars = list(expl_vars.mat.xs('calibrated'))
        if pc_ds is not None:
            pcds.pc_ds = pc_ds
        self.plot_data.append(pcds)

        return set_n + 1

    def update_color_level_data(self, set_id):
        '''To handle added level data to the active coloring_factor

        Will run throug the levels and create new datasources for each of the levels.
        Also hav to delet the old data source or check if it already exist

        Heuristics:
        Must indicate which dataset to copy values from. Can check if the size of
        the wanted axis index for the dataset is lager than the largest index in the Factor
        '''

        if self.coloring_factor is not None:
            self.coloring_factor.default_ds_axis = 'row'
            # Assumes rows with obj and col with PC
            pdata = self.plot_data[set_id - 1]
            pcds = pdata.pc_ds
            facname = self.coloring_factor.name

            for lvn in self.coloring_factor.levels:
                # get the subset row data for this level
                submx = self.coloring_factor.get_values(pcds, lvn)
                selT = submx.T
                # enumerate values to get the various PC vectors (one row for each PC)
                for i, pcvec in enumerate(selT, 1):
                    # Create keynames for vectors
                    # 'ds{}fc{}lv{}pc{}' ds nummer, factor name, level name, PC nummer
                    kn = "ds{0}:fc{1}:lv{2}:pc{3}".format(
                        set_id, facname, lvn, i)
                    # intert key-vector into plot data array or update if data already exist
                    self.arrays[kn] = pcvec
            # Create datasorce for the points not in a level
            submx = self.coloring_factor.get_rest_values(pcds)
            selT = submx.T
            for i, pcvec in enumerate(selT, 1):
                # Create keynames for vectors
                # 'ds{}fc{}lv{}pc{}' ds nummer, factor name, level name, PC nummer
                kn = "ds{0}:fc{1}:lv{2}:pc{3}".format(set_id, facname, 'not',
                                                      i)
                # intert key-vector into plot data array or update if data already exist
                self.arrays[kn] = pcvec
示例#9
0
class Dialog(MDialog, Window):
    """ The toolkit specific implementation of a Dialog.  See the IDialog
    interface for the API documentation.
    """

    #### 'IDialog' interface ##################################################

    cancel_label = Unicode

    help_id = Str

    help_label = Unicode

    ok_label = Unicode

    resizeable = Bool(True)

    return_code = Int(OK)

    style = Enum('modal', 'nonmodal')

    #### 'IWindow' interface ##################################################

    title = Unicode("Dialog")

    ###########################################################################
    # Protected 'IDialog' interface.
    ###########################################################################

    def _create_buttons(self, parent):
        buttons = QtGui.QDialogButtonBox()

        # 'OK' button.
        if self.ok_label:
            btn = buttons.addButton(self.ok_label,
                                    QtGui.QDialogButtonBox.AcceptRole)
        else:
            btn = buttons.addButton(QtGui.QDialogButtonBox.Ok)

        btn.setDefault(True)
        QtCore.QObject.connect(btn, QtCore.SIGNAL('clicked()'), self.control,
                               QtCore.SLOT('accept()'))

        # 'Cancel' button.
        if self.cancel_label:
            btn = buttons.addButton(self.cancel_label,
                                    QtGui.QDialogButtonBox.RejectRole)
        else:
            btn = buttons.addButton(QtGui.QDialogButtonBox.Cancel)

        QtCore.QObject.connect(btn, QtCore.SIGNAL('clicked()'), self.control,
                               QtCore.SLOT('reject()'))

        # 'Help' button.
        # FIXME v3: In the original code the only possible hook into the help
        # was to reimplement self._on_help().  However this was a private
        # method.  Obviously nobody uses the Help button.  For the moment we
        # display it but can't actually use it.
        if len(self.help_id) > 0:
            if self.help_label:
                buttons.addButton(self.help_label,
                                  QtGui.QDialogButtonBox.HelpRole)
            else:
                buttons.addButton(QtGui.QDialogButtonBox.Help)

        return buttons

    def _create_contents(self, parent):
        layout = QtGui.QVBoxLayout()

        if not self.resizeable:
            layout.setSizeConstraint(QtGui.QLayout.SetFixedSize)

        layout.addWidget(self._create_dialog_area(parent))
        layout.addWidget(self._create_buttons(parent))

        parent.setLayout(layout)

    def _create_dialog_area(self, parent):
        panel = QtGui.QWidget(parent)
        panel.setMinimumSize(QtCore.QSize(100, 200))

        palette = panel.palette()
        palette.setColor(QtGui.QPalette.Window, QtGui.QColor('red'))
        panel.setPalette(palette)
        panel.setAutoFillBackground(True)

        return panel

    def _show_modal(self):
        self.control.setWindowModality(QtCore.Qt.ApplicationModal)
        retval = self.control.exec_()
        return _RESULT_MAP[retval]

    ###########################################################################
    # Protected 'IWidget' interface.
    ###########################################################################

    def _create_control(self, parent):
        dlg = QtGui.QDialog(parent)

        # Setting return code and firing close events is handled for 'modal' in
        # MDialog's open method. For 'nonmodal', we do it here.
        if self.style == 'nonmodal':
            QtCore.QObject.connect(dlg, QtCore.SIGNAL('finished(int)'),
                                   self._finished_fired)

        if self.size != (-1, -1):
            dlg.resize(*self.size)

        dlg.setWindowTitle(self.title)

        return dlg

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _finished_fired(self, result):
        """ Called when the dialog is closed (and nonmodal). """

        self.return_code = _RESULT_MAP[result]
        self.close()
示例#10
0
class SubprocessWorkflow(Workflow):
    """A subclass of WorkflowSolver that spawns a subprocess to
     evaluate a single point."""

    #: File path of the Workflow object
    workflow_filepath = File(transient=True)

    #: The path to the force_bdss executable
    executable_path = Unicode(transient=True)

    def _call_subprocess(self, command, user_input):
        """Calls a subprocess to perform a command with parsed
        user_input"""

        log.info("Spawning subprocess: {}".format(command))

        # Setting ETS_TOOLKIT=null before executing bdss prevents it
        # from trying to create GUI every call, giving reducing the
        # overhead by a factor of 2.
        env = {**os.environ, "ETS_TOOLKIT": "null"}

        #scrivo il file data.txt con i dati di input
        rw_out = FileReaderWriter("./data.txt")
        d_out = {}
        d_keys = ["force.X", "force.Y"]
        for i in range(len(d_keys)):
            d_out[d_keys[i]] = user_input[i]
        rw_out.writeData(d_out)

        # Spawn the single point evaluation, which is the bdss itself
        # with the option evaluate.
        # We pass the specific parameter values via stdin, and read
        # the result via stdout.

        process = subprocess.Popen(command,
                                   env=env,
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

        log.info("Sending values: {}".format(user_input))
        stdout, stderr = process.communicate(
            " ".join(user_input).encode("utf-8"))
        print("stdout = ")
        print(stdout)
        print("stderr = ")
        print(stderr)
        return stdout

    def _subprocess_evaluate(self, parameter_values, filepath):
        """Executes the workflow using the given parameter values
        running on an external process via the subprocess library.
        Values for each parameter in thw workflow to calculate a
        single point
        """

        # This command calls a force_bdss executable on another process
        # to evaluate workflow serialized in filepath at a state determined
        # by the parameter values. A BaseMCOCommunicator will be needed
        # to be defined in the workflow to receive the data and send
        # back values corresponding to each KPI via the command line.
        command = [
            self.executable_path, "--logfile", "bdss.log", "--evaluate",
            filepath
        ]

        # Converts the parameter values to a string to send via
        # subprocess
        string_values = [str(v) for v in parameter_values]
        print("string_values = ")
        print(string_values)
        # Call subprocess to perform executable with user input
        stdout = self._call_subprocess(command, string_values)

        # Decode stdout into KPI float values
        #kpi_values = [float(x) for x in stdout.decode("utf-8").split()]
        kpi_values = [str(x) for x in stdout.decode("utf-8").split()]
        kpi_values = []
        for x in stdout.decode("utf-8").split():
            try:
                kpi_values.append(float(x))
            except:
                pass

        return kpi_values

    def evaluate(self, parameter_values):
        """Public method to evaluate the workflow at a given set of
        MCO parameter values

        Parameters
        ----------
        parameter_values: iterable
            List of values to assign to each BaseMCOParameter defined
            in the workflow

        Returns
        -------
        kpi_results: list
            List of values corresponding to each MCO KPI in the
            workflow
        """

        try:
            # If a path to a workflow file is assigned, then use this
            # as a reference, otherwise generate a temporary file and
            # save a copy of this Workflow instance there
            if self.workflow_filepath:
                #print("1")
                #print("parameter_values=")
                #print(parameter_values)
                return self._subprocess_evaluate(parameter_values,
                                                 self.workflow_filepath)
            else:
                #print("2")
                with tempfile.NamedTemporaryFile() as tmp_file:
                    #print("2.1")
                    #print("parameter_values=")
                    #print(parameter_values)
                    writer = WorkflowWriter()
                    #writer.write(self, tmp_file.name)
                    temp_filename = os.getcwd()
                    plugin_path = os.path.join(os.getcwd(),
                                               "force-bdss-plugin-es-example")
                    if (os.path.exists(plugin_path)):
                        temp_filename = os.path.join(plugin_path, "tmp.txt")
                    else:
                        temp_filename = os.path.join(os.getcwd(), "tmp.txt")
                    writer.write(self, temp_filename)
                    #writer.write(self, "D:\\users\\Progetti\\GitHub\\Force-Project\\force-bdss-plugin-es-example\\tmp.txt")
                    #return self._subprocess_evaluate(
                    #    parameter_values, tmp_file.name)
                    #return self._subprocess_evaluate(
                    #    parameter_values, "D:\\users\\Progetti\\GitHub\\Force-Project\\force-bdss-plugin-es-example\\tmp.txt")
                    return self._subprocess_evaluate(parameter_values,
                                                     temp_filename)

        except Exception:
            message = ('SubprocessWorkflow failed '
                       'to run. This is likely due to an error in the '
                       'BaseMCOCommunicator assigned to {}.'.format(
                           self.mco_model.factory.__class__))
            log.exception(message)
            raise RuntimeError(message)
示例#11
0
class SubprocessWorkflowEvaluator(WorkflowEvaluator):
    """A subclass of WorkflowSolver that spawns a subprocess to
     evaluate a single point."""

    #: The path to the force_bdss executable
    executable_path = Unicode()

    def _call_subprocess(self, command, user_input):
        """Calls a subprocess to perform a command with parsed
        user_input"""

        log.info("Spawning subprocess: {}".format(command))

        # Setting ETS_TOOLKIT=null before executing bdss prevents it
        # from trying to create GUI every call, giving reducing the
        # overhead by a factor of 2.
        env = {**os.environ, "ETS_TOOLKIT": "null"}

        # Spawn the single point evaluation, which is the bdss itself
        # with the option evaluate.
        # We pass the specific parameter values via stdin, and read
        # the result via stdout.
        process = subprocess.Popen(
            command,
            env=env,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )

        log.info("Sending values: {}".format(user_input))
        stdout, stderr = process.communicate(
            " ".join(user_input).encode("utf-8")
        )

        return stdout

    def _subprocess_evaluate(self, parameter_values):
        """Executes the workflow using the given parameter values
        running on an external process via the subprocess library.
        Values for each parameter in thw workflow to calculate a
        single point
        """

        # This command calls a force_bdss executable on another process
        # to evaluate the same workflow at a state determined by the
        # parameter values. A BaseMCOCommunicator will be needed to be
        # defined in the workflow to receive the data and send back values
        # corresponding to each KPI via the command line.
        command = [self.executable_path,
                   "--logfile",
                   "bdss.log",
                   "--evaluate",
                   self.workflow_filepath]

        # Converts the parameter values to a string to send via
        # subprocess
        string_values = [str(v) for v in parameter_values]

        # Call subprocess to perform executable with user input
        stdout = self._call_subprocess(command, string_values)

        # Decode stdout into KPI float values
        kpi_values = [float(x) for x in stdout.decode("utf-8").split()]

        return kpi_values

    def evaluate(self, parameter_values):
        """Public method to evaluate the workflow at a given set of
        MCO parameter values

        Parameters
        ----------
        parameter_values: list
            List of values to assign to each BaseMCOParameter defined
            in the workflow

        Returns
        -------
        kpi_results: list
            List of values corresponding to each MCO KPI in the
            workflow
        """

        try:
            return self._subprocess_evaluate(parameter_values)

        except Exception:
            message = (
                'SubprocessWorkflowEvaluator failed '
                'to run. This is likely due to an error in the '
                'BaseMCOCommunicator assigned to {}.'.format(
                    self.mco_model.factory.__class__)
            )
            log.exception(message)
            raise RuntimeError(message)
示例#12
0
class CtfManager(HasStrictTraits):
    """ Simple manager for loading and saving CTF files to a directory.
    """

    # The root directory with the files.
    root_dir = Unicode('.')

    # The available CTFs.
    names = List(Unicode)

    # The transfer functions by name.
    functions = Dict(Unicode, Instance(TransferFunction))

    @classmethod
    def from_directory(cls, root_dir, **traits):
        """ Create a new instance using the files in directory `root_dir`
        """
        manager = cls(root_dir=root_dir, **traits)
        manager._read_from_dir()
        return manager

    def add(self, name, transfer_func):
        """ Add a transfer function with the given name.

        Parameters
        ----------
        name : str
            The name of the transfer function.
        transfer_func : TransferFunction
            A transfer function instance
        """
        encoded_name = _name_encode(name)
        fn = os.path.join(self.root_dir, encoded_name + CTF_EXTENSION)
        if not os.path.isdir(self.root_dir):
            os.makedirs(self.root_dir)

        save_ctf(transfer_func, fn)
        self.functions[name] = transfer_func
        self._update_names()

    def get(self, name):
        """ Return a function for the given name.

        Parameters
        ----------
        name : str
            The name of the function.

        Returns
        -------
        transfer_func : TransferFunction
            A transfer function.
        """
        return self.functions.get(name).copy()

    def _read_from_dir(self):
        ctfs = glob.glob(os.path.join(self.root_dir, '*' + CTF_EXTENSION))
        funcs = {}
        for fn in ctfs:
            name = os.path.splitext(os.path.basename(fn))[0]
            decoded_name = _name_decode(name)
            funcs[decoded_name] = load_ctf(fn)
        self.functions = funcs
        self._update_names()

    def _update_names(self):
        self.names = sorted(self.functions)
示例#13
0
class ApplicationWindow(MApplicationWindow, Window):
    """ The toolkit specific implementation of an ApplicationWindow.  See the
    IApplicationWindow interface for the API documentation.
    """

    #### 'IApplicationWindow' interface #######################################

    icon = Instance(ImageResource)

    menu_bar_manager = Instance(MenuBarManager)

    status_bar_manager = Instance(StatusBarManager)

    tool_bar_manager = Instance(ToolBarManager)

    # If the underlying toolkit supports multiple toolbars then you can use
    # this list instead.
    tool_bar_managers = List(ToolBarManager)

    #### 'IWindow' interface ##################################################

    # fixme: We can't set the default value of the actual 'size' trait here as
    # in the toolkit-specific event handlers for window size and position
    # changes, we set the value of the shadow '_size' trait. The problem is
    # that by doing that traits never knows that the trait has been set and
    # hence always returns the default value! Using a trait initializer method
    # seems to work however (e.g. 'def _size_default'). Hmmmm....
    ##     size = (800, 600)

    title = Unicode("Pyface")

    ###########################################################################
    # Protected 'IApplicationWindow' interface.
    ###########################################################################

    def _create_contents(self, parent):
        panel = wx.Panel(parent, -1, name="ApplicationWindow")
        panel.SetSize((500, 400))
        panel.SetBackgroundColour('blue')

        return panel

    def _create_menu_bar(self, parent):
        if self.menu_bar_manager is not None:
            menu_bar = self.menu_bar_manager.create_menu_bar(parent)
            self.control.SetMenuBar(menu_bar)

    def _create_status_bar(self, parent):
        if self.status_bar_manager is not None:
            status_bar = self.status_bar_manager.create_status_bar(parent)
            self.control.SetStatusBar(status_bar)

        return

    def _create_tool_bar(self, parent):
        tool_bar_managers = self._get_tool_bar_managers()
        if len(tool_bar_managers) > 0:
            for tool_bar_manager in reversed(tool_bar_managers):
                tool_bar = tool_bar_manager.create_tool_bar(parent)
                self._add_toolbar_to_aui_manager(tool_bar)
            self._aui_manager.Update()

    def _set_window_icon(self):
        if self.icon is None:
            icon = ImageResource('application.ico')
        else:
            icon = self.icon

        if self.control is not None:
            self.control.SetIcon(icon.create_icon())

        return

    ###########################################################################
    # 'Window' interface.
    ###########################################################################

    def _size_default(self):
        """ Trait initialiser. """

        return (800, 600)

    ###########################################################################
    # Protected 'IWidget' interface.
    ###########################################################################

    def _create(self):

        super(ApplicationWindow, self)._create()

        self._aui_manager = PyfaceAuiManager()
        self._aui_manager.SetManagedWindow(self.control)

        # Keep a reference to the AUI Manager in the control because Panes
        # will need to access it in order to lay themselves out
        self.control._aui_manager = self._aui_manager

        contents = self._create_contents(self.control)

        self._create_trim_widgets(self.control)

        # Updating the AUI manager actually commits all of the pane's added
        # to it (this allows batch updates).
        self._aui_manager.Update()

        return

    def _create_control(self, parent):

        style = wx.DEFAULT_FRAME_STYLE \
                | wx.FRAME_NO_WINDOW_MENU \
                | wx.CLIP_CHILDREN

        control = wx.Frame(parent,
                           -1,
                           self.title,
                           style=style,
                           size=self.size,
                           pos=self.position)

        # Mac/Win needs this, otherwise background color is black
        attr = control.GetDefaultAttributes()
        control.SetBackgroundColour(attr.colBg)

        return control

    def destroy(self):
        if self.control:
            self._aui_manager.UnInit()
        super(ApplicationWindow, self).destroy()

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _add_toolbar_to_aui_manager(self, tool_bar):
        """ Add a toolbar to the AUI manager. """

        info = self._get_tool_par_pane_info(tool_bar)
        self._aui_manager.AddPane(tool_bar, info)

        return

    def _get_tool_par_pane_info(self, tool_bar):
        info = aui.AuiPaneInfo()
        info.Caption(tool_bar.tool_bar_manager.name)
        info.LeftDockable(False)
        info.Name(tool_bar.tool_bar_manager.id)
        info.RightDockable(False)
        info.ToolbarPane()
        info.Top()

        return info

    def _get_tool_bar_managers(self):
        """ Return all tool bar managers specified for the window. """

        # fixme: V3 remove the old-style single toolbar option!
        if self.tool_bar_manager is not None:
            tool_bar_managers = [self.tool_bar_manager]

        else:
            tool_bar_managers = self.tool_bar_managers

        return tool_bar_managers

    def _wx_enable_tool_bar(self, tool_bar, enabled):
        """ Enable/Disablea tool bar. """

        # AUI toolbars cannot be enabled/disabled.

        return

    def _wx_show_tool_bar(self, tool_bar, visible):
        """ Hide/Show a tool bar. """

        pane = self._aui_manager.GetPane(tool_bar.tool_bar_manager.id)

        if visible:
            pane.Show()

        else:
            # Without this workaround, toolbars know the sizes of other
            # hidden toolbars and leave gaps in the toolbar dock
            pane.window.Show(False)
            self._aui_manager.DetachPane(pane.window)
            info = self._get_tool_par_pane_info(pane.window)
            info.Hide()
            self._aui_manager.AddPane(pane.window, info)

        self._aui_manager.Update()

        return

    #### Trait change handlers ################################################

    def _menu_bar_manager_changed(self):
        if self.control is not None:
            self._create_menu_bar(self.control)

    def _status_bar_manager_changed(self, old, new):
        if self.control is not None:
            if old is not None:
                self.control.SetStatusBar(None)
                old.remove_status_bar(self.control)
            self._create_status_bar(self.control)

    @on_trait_change('tool_bar_manager, tool_bar_managers')
    def _update_tool_bar_managers(self):
        if self.control is not None:
            self._create_tool_bar(self.control)

    def _icon_changed(self):
        self._set_window_icon()
class ProgressDialog(MProgressDialog, Window):
    """ A simple progress dialog window which allows itself to be updated

    """
    # FIXME: buttons are not set up correctly yet

    #: The progress bar widget
    progress_bar = Instance(QtGui.QProgressBar)

    #: The window title
    title = Unicode

    #: The text message to display in the dialog
    message = Unicode

    #: The minimum value of the progress range
    min = Int

    #: The minimum value of the progress range
    max = Int

    #: The margin around the progress bar
    margin = Int(5)

    #: Whether or not the progress dialog can be cancelled
    can_cancel = Bool(False)

    # The IProgressDialog interface doesn't declare this, but since this is a
    # feature of the QT backend ProgressDialog that doesn't appear in WX, we
    # offer an option to disable it.
    can_ok = Bool(False)

    #: Whether or not to show the time taken (not implemented in Qt)
    show_time = Bool(False)

    #: Whether or not to show the percent completed
    show_percent = Bool(False)

    #: The size of the dialog
    dialog_size = Instance(QtCore.QRect)

    #: Label for the 'cancel' button
    cancel_button_label = Unicode('Cancel')

    #: Whether or not the dialog was cancelled by the user
    _user_cancelled = Bool(False)

    #: The widget showing the message text
    _message_control = Instance(QtGui.QLabel)

    #: The widget showing the time elapsed
    _elapsed_control = Instance(QtGui.QLabel)

    #: The widget showing the estimated time to completion
    _estimated_control = Instance(QtGui.QLabel)

    #: The widget showing the estimated time remaining
    _remaining_control = Instance(QtGui.QLabel)

    #-------------------------------------------------------------------------
    # IWindow Interface
    #-------------------------------------------------------------------------

    def open(self):
        """ Opens the window. """
        super(ProgressDialog, self).open()
        self._start_time = time.time()

    def close(self):
        """ Closes the window. """
        self.progress_bar.destroy()
        self.progress_bar = None

        super(ProgressDialog, self).close()

    #-------------------------------------------------------------------------
    # IProgressDialog Interface
    #-------------------------------------------------------------------------

    def update(self, value):
        """ Update the progress bar to the desired value

        If the value is >= the maximum and the progress bar is not contained
        in another panel the parent window will be closed.

        Parameters
        ----------
        value :
            The progress value to set.
        """

        if self.progress_bar is None:
            return None, None

        if self.max > 0:
            self.progress_bar.setValue(value)

            if (self.max != self.min):
                percent = (float(value) - self.min) / (self.max - self.min)
            else:
                percent = 1.0

            if self.show_time and (percent != 0):
                current_time = time.time()
                elapsed = current_time - self._start_time
                estimated = elapsed / percent
                remaining = estimated - elapsed

                self._set_time_label(elapsed, self._elapsed_control)
                self._set_time_label(estimated, self._estimated_control)
                self._set_time_label(remaining, self._remaining_control)

            if value >= self.max or self._user_cancelled:
                self.close()
        else:
            self.progress_bar.setValue(self.progress_bar.value() + value)

            if self._user_cancelled:
                self.close()

        QtGui.QApplication.processEvents()

        return (not self._user_cancelled, False)

    #-------------------------------------------------------------------------
    # Private Interface
    #-------------------------------------------------------------------------

    def reject(self, event):
        self._user_cancelled = True
        self.close()

    def _set_time_label(self, value, control):
        hours = value / 3600
        minutes = (value % 3600) / 60
        seconds = value % 60
        label = "%1u:%02u:%02u" % (hours, minutes, seconds)

        control.setText(label)

    def _create_buttons(self, dialog, layout):
        """ Creates the buttons. """

        if not (self.can_cancel or self.can_ok):
            return

        # Create the button.
        buttons = QtGui.QDialogButtonBox()

        if self.can_cancel:
            buttons.addButton(self.cancel_button_label,
                              QtGui.QDialogButtonBox.RejectRole)
        if self.can_ok:
            buttons.addButton(QtGui.QDialogButtonBox.Ok)

        # TODO: hookup the buttons to our methods, this may involve subclassing from QDialog

        if self.can_cancel:
            buttons.rejected.connect(dialog.reject)
        if self.can_ok:
            buttons.accepted.connect(dialog.accept)

        layout.addWidget(buttons)

    def _create_label(self, dialog, layout, text):

        dummy = QtGui.QLabel(text, dialog)
        dummy.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)

        label = QtGui.QLabel("unknown", dialog)
        label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft
                           | QtCore.Qt.AlignRight)

        sub_layout = QtGui.QHBoxLayout()

        sub_layout.addWidget(dummy)
        sub_layout.addWidget(label)

        layout.addLayout(sub_layout)

        return label

    def _create_gauge(self, dialog, layout):

        self.progress_bar = QtGui.QProgressBar(dialog)
        self.progress_bar.setRange(self.min, self.max)
        layout.addWidget(self.progress_bar)

        if self.show_percent:
            self.progress_bar.setFormat("%p%")
        else:
            self.progress_bar.setFormat("%v")

    def _create_message(self, dialog, layout):
        label = QtGui.QLabel(self.message, dialog)
        label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
        layout.addWidget(label)
        self._message_control = label
        return

    def _create_percent(self, dialog, parent_sizer):
        if not self.show_percent:
            return

        raise NotImplementedError

    def _create_timer(self, dialog, layout):
        if not self.show_time:
            return

        self._elapsed_control = self._create_label(dialog, layout,
                                                   "Elapsed time : ")
        self._estimated_control = self._create_label(dialog, layout,
                                                     "Estimated time : ")
        self._remaining_control = self._create_label(dialog, layout,
                                                     "Remaining time : ")

    def _create_control(self, parent):
        return QtGui.QDialog(parent)

    def _create(self):
        super(ProgressDialog, self)._create()
        self._create_contents(self.control)

    def _create_contents(self, parent):
        dialog = parent
        layout = QtGui.QVBoxLayout(dialog)
        layout.setContentsMargins(self.margin, self.margin, self.margin,
                                  self.margin)

        # The 'guts' of the dialog.
        self._create_message(dialog, layout)
        self._create_gauge(dialog, layout)
        self._create_timer(dialog, layout)
        self._create_buttons(dialog, layout)

        dialog.setWindowTitle(self.title)

        parent.setLayout(layout)

    #-------------------------------------------------------------------------
    # Trait change handlers
    #-------------------------------------------------------------------------

    def _max_changed(self, new):
        if self.progress_bar is not None:
            self.progress_bar.setMaximum(new)

    def _min_changed(self, new):
        if self.progress_bar is not None:
            self.progress_bar.setMinimum(new)

    def _message_changed(self, new):
        if self._message_control is not None:
            self._message_control.setText(new)
示例#15
0
class SurfactantSimulationBuilder(BaseGromacsSimulationBuilder):

    # --------------------
    #  Required Attributes
    # --------------------

    #: List of all Ingredient objects
    formulation = Instance(Formulation)

    #: File path for Gromacs energy minimization parameters
    minimize_parameters = Unicode()

    #: File path for Gromacs prodcution run parameters
    production_parameters = Unicode()

    # --------------------
    #  Regular Attributes
    # --------------------

    #: Folder for data regarding initialisation of simulation
    init_folder = Unicode()

    #: Folder for data regarding solvation of simulation
    solvate_folder = Unicode()

    #: Folder for data regarding add ions to simulation
    ions_folder = Unicode()

    #: Folder for data regarding energy minimisation
    #: simulation
    minimize_folder = Unicode()

    #: Folder for data regarding simulation production run
    production_folder = Unicode()

    # Results file name containing surfactant trajectory coordinates for
    # further postprocessing
    results_file = Unicode()

    # --------------------
    #     Properties
    # --------------------

    #: List of surfactant ingredients
    surfactants = Property(
        List(Ingredient),
        depends_on='formulation.[concentrations,ingredients.role]')

    #: List of salt ingredients
    salts = Property(
        List(Ingredient),
        depends_on='formulation.[concentrations,ingredients.role]')

    #: Solvent ingredient
    solvent = Property(
        Instance(Ingredient),
        depends_on='formulation.[concentrations,ingredients.role]')

    def __init__(self, *args, **kwargs):
        super(SurfactantSimulationBuilder, self).__init__(*args, **kwargs)
        # Initialise ingredient concentrations and number of
        # molecules to add to simulation
        self._update_n_mols()

    # --------------------
    #      Defaults
    # --------------------

    def _init_folder_default(self):
        return os.path.join(self.folder, '1_init')

    def _solvate_folder_default(self):
        return os.path.join(self.folder, '2_solvate')

    def _ions_folder_default(self):
        return os.path.join(self.folder, '3_ions')

    def _minimize_folder_default(self):
        return os.path.join(self.folder, '4_minimize')

    def _production_folder_default(self):
        return os.path.join(self.folder, '5_production')

    def _results_file_default(self):
        return self.name + '_results'

    # --------------------
    #      Listeners
    # --------------------

    def _get_surfactants(self):
        return self.sort_ingredients('Surfactant')

    def _get_salts(self):
        return self.sort_ingredients('Salt')

    def _get_solvent(self):
        return self.sort_ingredients('Solvent')[0]

    # --------------------
    #    Private Methods
    # --------------------

    def _update_n_mols(self):
        """Calculates number of fragments required in simulation
        for each solvent, salt and surfactant and updates their
        respective ingredient objects n_mol attribute"""

        n_mols = calculate_n_mols(self.size, self.formulation.num_fractions)

        n_ion = 0
        for n_mol, ingredient in zip(n_mols, self.formulation.ingredients):
            ingredient.n_mol = n_mol

            # Track the number of ions that will replace solvent
            # molecules
            if ingredient.role == 'Surfactant':
                n_ion += n_mol * (len(ingredient.fragments) - 1)
            elif ingredient.role == 'Salt':
                n_ion += n_mol * len(ingredient.fragments)

        # Check that total solvent molecule count is greater than the
        # number of ions required (these solvent fragments will be replaced
        # by ions in the pre-processing)
        assert self.solvent.n_mol > n_ion, (
            'Invalid number of solvent fragments '
            f'{self.solvent.name}: {self.solvent.n_mol}'
            f' < {n_ion}. '
            'Check input concentrations of ingredients')

    def _update_cell_dim(self, density=1.0):
        """Calculates the size of the simulation cell in nm based
        on an estimation of the formulation density in g cm-3
        """

        # Calculate masses of each ingredient in g
        masses = [
            ingredient.n_mol * ingredient.mass / N_A
            for ingredient in self.formulation.ingredients
        ]

        # Calculate cell volume in nm3
        cell_volume = 1E21 * sum(masses) / density

        # Calculate each dimension in nm, assuming a square cell geometry
        self.cell_dim = [cell_volume**(1 / 3) for _ in range(3)]

    def _update_topology_data(self, topology=None, symbol=None, n_mol=0):
        """Updates attribute _topology_data, which stores data to write
        to a human readable Gromacs topology file"""

        if topology is not None:
            self.topology_data.add_molecule_file(topology)

        if symbol is not None:
            self.topology_data.add_fragment(symbol)
            self.topology_data.edit_fragment_number(symbol, n_mol)

    def _add_surfactant(self, input_coordinate, surfactant_coord, n_mol,
                        output_coordinate, tag):
        """Add surfactant molecule to simulation cell"""
        self._pipeline.append((f'insert_molecules_{tag}',
                               Gromacs_insert_molecules(command_options={
                                   '-f': input_coordinate,
                                   '-ci': surfactant_coord,
                                   '-nmol': n_mol,
                                   '-o': output_coordinate,
                                   '-box': self.cell_dim
                               }, )))

    def _add_solvent(self, input_coordinate, solvent_coord, n_mol,
                     output_coordinate, tag):
        """Add solvent molecule to simulation cell"""
        self._pipeline.append((f'solvate_{tag}',
                               Gromacs_solvate(
                                   command_options={
                                       '-cp': input_coordinate,
                                       '-cs': solvent_coord,
                                       '-maxsol': n_mol,
                                       '-o': output_coordinate
                                   })))

    def _add_ions(self, ions, n_mol, input_binary, input_topology,
                  output_coordinate, tag):
        """Add atomic ions to simulation cell"""
        command_options = {
            '-s': input_binary,
            '-p': input_topology,
            '-o': output_coordinate
        }

        for ion in ions:
            if ion.charge > 0:
                command_options["-pname"] = ion.symbol
                command_options["-np"] = n_mol
                command_options["-pq"] = int(ion.charge)
            else:
                command_options["-nname"] = ion.symbol
                command_options["-nn"] = n_mol
                command_options["-nq"] = int(ion.charge)

        self._pipeline.append((f'genion_{tag}',
                               Gromacs_genion(command_options=command_options,
                                              user_input='W')))

    def _add_mdrun(self,
                   input_binary,
                   output_energy,
                   output_traj,
                   output_coordinate,
                   output_log,
                   output_state,
                   output_comp_traj,
                   tag,
                   n_steps=-2):
        """Add MD simulation run to pipeline"""
        self._pipeline.append((f'mdrun_{tag}',
                               Gromacs_mdrun(command_options={
                                   '-s': input_binary,
                                   '-o': output_traj,
                                   '-e': output_energy,
                                   '-c': output_coordinate,
                                   '-g': output_log,
                                   '-cpo': output_state,
                                   '-x': output_comp_traj,
                                   '-nsteps': n_steps
                               },
                                             mpi_run=self.mpi_run)))

    def _add_trjconv(self, input_traj, input_topology, output_coord,
                     index_file):
        """Prepares trajectory for clustering analysis, similar to
        http://www.gromacs.org/Documentation/How-tos/Micelle_Clustering"""

        selection = [
            f'( resname {surfactant.fragments[0].symbol} )'
            for surfactant in self.surfactants
        ]
        selection = ' or '.join(selection)

        self._pipeline.append((f'g_select',
                               Gromacs_select(command_options={
                                   '-f': input_traj,
                                   '-s': input_topology,
                                   '-on': index_file
                               },
                                              user_input=selection)))

        self._pipeline.append((f'trjconv_nojump',
                               Gromacs_trjconv(
                                   command_options={
                                       '-f': input_traj,
                                       '-s': input_topology,
                                       '-o': output_coord,
                                       '-pbc': 'nojump',
                                       '-n': index_file
                                   })))

        self._pipeline.append((f'trjconv_whole',
                               Gromacs_trjconv(
                                   command_options={
                                       '-f': output_coord,
                                       '-s': input_topology,
                                       '-o': output_coord,
                                       '-pbc': 'whole',
                                       '-n': index_file
                                   })))

    def _make_binary(self, input_topology, input_coordinate, output_binary,
                     md_parameters, tag):
        """Add generation of binary topology file to pipeline"""

        output_topology = os.path.splitext(input_topology)[0] + '.mdp'
        self._pipeline.append((f'grompp_{tag}',
                               Gromacs_grompp(
                                   command_options={
                                       '-f': md_parameters,
                                       '-p': input_topology,
                                       '-c': input_coordinate,
                                       '-o': output_binary,
                                       '-maxwarn': 4,
                                       '-po': output_topology
                                   })))

    def _make_topology(self):
        """Add generation of human readable topology file to pipeline"""
        self._pipeline.append(
            ('top_file',
             GromacsTopologyWriter(topology_data=self.topology_data,
                                   sim_name=self.name,
                                   directory=self.folder,
                                   top_name=self.file_registry.top_file)))

    def _file_tree_builder(self):
        """Add generation of file tree simulation files to be stored
        in to pipeline"""

        self._pipeline.append(('file_tree',
                               FileTreeBuilder(
                                   directory=self.folder,
                                   folders=[
                                       '1_init', '2_solvate', '3_ions',
                                       '4_minimize', '5_production'
                                   ],
                               )))

    def _init_simulation(self):
        """Write scripts / commands to initialise the simulation
        with surfactant molecules"""

        # Initialise coordinate file with Primary Surfactant
        surfactant = self.surfactants[0]
        input_coordinate = surfactant.fragments[0].coordinate
        output_coordinate = os.path.join(self.init_folder,
                                         self.file_registry.coord_file)

        self._add_surfactant(input_coordinate, input_coordinate,
                             surfactant.n_mol - 1, output_coordinate,
                             'surfactant_0')

        self._update_topology_data(surfactant.fragments[0].topology,
                                   surfactant.fragments[0].symbol,
                                   surfactant.n_mol)

        # Add Secondary Surfactants (optional) to coordinate file
        input_coordinate = output_coordinate
        for i, surfactant in enumerate(self.surfactants[1:]):
            self._add_surfactant(input_coordinate,
                                 surfactant.fragments[0].coordinate,
                                 surfactant.n_mol, output_coordinate,
                                 f'surfactant_{i + 1}')

            self._update_topology_data(surfactant.fragments[0].topology,
                                       surfactant.fragments[0].symbol,
                                       surfactant.n_mol)

    def _solvate_simulation(self):

        # Solvate simulation
        input_coordinate = os.path.join(self.init_folder,
                                        self.file_registry.coord_file)
        output_coordinate = os.path.join(self.solvate_folder,
                                         self.file_registry.coord_file)

        for i, fragment in enumerate(self.solvent.fragments):
            self._add_solvent(input_coordinate, fragment.coordinate,
                              self.solvent.n_mol, output_coordinate,
                              f'solvent_{i}')

            self._update_topology_data(fragment.topology, fragment.symbol,
                                       self.solvent.n_mol)

    def _ions_simulation(self):

        top_file = self.file_registry.top_file
        coord_file = self.file_registry.coord_file
        binary_file = self.file_registry.binary_file

        input_topology = os.path.join(self.folder, top_file)
        input_coordinate = os.path.join(self.solvate_folder, coord_file)
        output_coordinate = os.path.join(self.ions_folder, coord_file)
        input_binary = os.path.join(self.ions_folder, binary_file)
        output_binary = input_binary

        # Add all the salt ion topologies to the main Gromacs
        # topology file
        for salt in self.salts:
            for ion in salt.fragments:
                self._update_topology_data(ion.topology)

        # Create human readable topology file with fragment ingredient
        # that have been inserted into simulation box
        self._make_topology()

        # Add binary file step to pipeline. This will be used to insert
        # ions using Gromacs genion command
        self._make_binary(input_topology, input_coordinate, output_binary,
                          self.minimize_parameters, 'ions_0')

        # Replace some Solvent species with surfactant counter-ion
        for i, surfactant in enumerate(self.surfactants):
            if len(surfactant.fragments) > 1:
                self._add_ions(surfactant.fragments[1:], surfactant.n_mol,
                               input_binary, input_topology, output_coordinate,
                               f'surfactant_{i}')

                # Update binary topology file
                self._make_binary(input_topology, output_coordinate,
                                  output_binary, self.minimize_parameters,
                                  f'surfactant_ions_{i}')

        # Replace some Solvent species with salt ions
        for i, salt in enumerate(self.salts):
            self._add_ions(salt.fragments, salt.n_mol, input_binary,
                           input_topology, output_coordinate, f'salt_{i}')

            # Update binary topology file
            self._make_binary(input_topology, output_coordinate, output_binary,
                              self.minimize_parameters, f'salt_ions_{i}')

    def _minimize_simulation(self):

        coord_file = self.file_registry.coord_file
        binary_file = self.file_registry.binary_file
        energy_file = self.file_registry.energy_file
        log_file = self.file_registry.log_file
        traj_file = self.file_registry.traj_file
        comp_traj_file = self.file_registry.comp_traj_file
        state_file = self.file_registry.state_file

        output_coordinate = os.path.join(self.minimize_folder, coord_file)
        input_binary = os.path.join(self.ions_folder, binary_file)
        output_energy = os.path.join(self.minimize_folder, energy_file)
        output_log = os.path.join(self.minimize_folder, log_file)
        output_traj = os.path.join(self.minimize_folder, traj_file)
        output_comp_traj = os.path.join(self.minimize_folder, comp_traj_file)
        output_state = os.path.join(self.minimize_folder, state_file)

        # Run an energy minimization
        self._add_mdrun(input_binary, output_energy, output_traj,
                        output_coordinate, output_log, output_state,
                        output_comp_traj, 'minimize')

    def _production_simulation(self):

        top_file = self.file_registry.top_file
        coord_file = self.file_registry.coord_file
        binary_file = self.file_registry.binary_file
        energy_file = self.file_registry.energy_file
        log_file = self.file_registry.log_file
        traj_file = self.file_registry.traj_file
        comp_traj_file = self.file_registry.comp_traj_file
        state_file = self.file_registry.state_file

        input_topology = os.path.join(self.folder, top_file)
        input_coordinate = os.path.join(self.minimize_folder, coord_file)
        output_coordinate = os.path.join(self.production_folder, coord_file)
        input_binary = os.path.join(self.production_folder, binary_file)
        output_binary = input_binary
        output_energy = os.path.join(self.production_folder, energy_file)
        output_log = os.path.join(self.production_folder, log_file)
        output_traj = os.path.join(self.production_folder, traj_file)
        output_comp_traj = os.path.join(self.production_folder, comp_traj_file)
        output_state = os.path.join(self.production_folder, state_file)

        # Update binary topology file
        self._make_binary(input_topology, input_coordinate, output_binary,
                          self.production_parameters, 'production')

        # Run an MD production run
        self._add_mdrun(input_binary,
                        output_energy,
                        output_traj,
                        output_coordinate,
                        output_log,
                        output_state,
                        output_comp_traj,
                        'production',
                        n_steps=self.n_steps)

    def _post_process_results(self):

        binary_file = self.file_registry.binary_file
        traj_file = self.file_registry.comp_traj_file

        input_topology = os.path.join(self.production_folder, binary_file)
        input_traj = os.path.join(self.production_folder, traj_file)

        # Perform postprocessing on binary trajectory to obtain human
        # readable file ready for further analysis
        index_file_name = self.file_registry.format_file_name(
            self.file_registry.traj_file, 'index')
        index_file = os.path.join(self.production_folder, index_file_name)

        output_file_path = self.get_results_path()

        self._add_trjconv(input_traj, input_topology, output_file_path,
                          index_file)

    # --------------------
    #    Public Methods
    # --------------------

    def sort_ingredients(self, key):
        """Function that extracts indices of roles that match
        the term key and returns a list of these ordered by their
        corresponding ingredient

        Parameters
        ----------
        key: str
            Key to match to Ingredient role

        Returns
        -------
        ordered_ingredients: list of Ingredient
            List of Ingredient objects where role==key ordered by
            concentration values (high to low)
        """

        # Extract fragments whose role == key, and group with their
        # ingredient parameters
        key_ingredients = self.formulation.ingredient_search([key])
        key_concentrations = [
            self.formulation.concentrations[self.formulation.ingredients.index(
                ingredient)] for ingredient in key_ingredients
        ]

        # Return an ordered version of ingredients, sorted by
        # concentrations (high to low)
        ordered_ingredients = [
            ingredient for _, ingredient in sorted(zip(key_concentrations,
                                                       key_ingredients),
                                                   key=itemgetter(0),
                                                   reverse=True)
        ]

        return ordered_ingredients

    def build_pipeline(self):

        # Recalculate ingredient concentration attributes
        self._update_n_mols()

        # Recalculate simulation cell dimensions
        self._update_cell_dim()

        # Reset pipeline and topology_data attributes
        delattr(self, '_pipeline')
        delattr(self, 'topology_data')
        self._update_topology_data(self.martini_parameters)

        # Add initial command to build a file tree for this simulation
        # data to be stored in
        self._file_tree_builder()

        # Initialise simulation cell with surfactant molecules
        self._init_simulation()

        # Solvate simulation cell with solvent molecules
        self._solvate_simulation()

        # Add ions to cell by swapping with solvent molecules
        self._ions_simulation()

        # Run energy minimization to reach acceptable starting
        # coordinates
        self._minimize_simulation()

        # Perform a production run simulation to calculate properties
        # from
        self._production_simulation()

        # Call Gromacs post processing utilities to prepare trajectory
        # file for clustering
        self._post_process_results()

        return self._pipeline

    def get_results_path(self):
        """Obtain the results trajectory file path for further
        post-processing"""

        file_path = f'{self.production_folder}/{self.results_file}'
        for surfactant in self.surfactants:
            file_path += f'_{surfactant.fragments[0].symbol}'
        file_path += '.gro'

        return file_path
示例#16
0
class ExecutionContext(Controller):
    '''
    An execution context contains all the information necessary to start a 
    job. For instance, in order to use FSL, it is necessary to setup a few
    environment variables whose content depends on the location where FSL 
    is installed. The execution context contains the information about FSL 
    installation necessary to define these environment variable when a job 
    is started. The execution context is shared with every processing nodes 
    and used to build the execution environment of each job.
    '''
    
    python_path_first = List(Unicode())
    python_path_last = List(Unicode)
    environ = Dict(Unicode(), Unicode())
    
    #def __init__(self, python_path_first=[], python_path_last=[],
                 #environ = {}):
        #self.python_path_first = python_path_first
        #self.python_path_last = python_path_last
        #self.environ = environ
    
    def to_json(self):
        '''
        Returns a dictionary containing JSON compatible representation of
        the execution context.
        '''
        kwargs = {}
        if self.python_path_first:
            kwargs['python_path_first'] = self.python_path_first
        if self.python_path_last:
            kwargs['python_path_last'] = self.python_path_last
        if self.environ:
            kwargs['environ'] = self.environ
        return ['capsul.engine.execution_context.from_json', kwargs]
        
    def __enter__(self):
        self._sys_path_first = [osp.expandvars(osp.expanduser(i)) 
                                for i in self.python_path_first]
        self._sys_path_last = [osp.expandvars(osp.expanduser(i)) 
                                for i in self.python_path_last]
        sys.path = self._sys_path_first + sys.path + self._sys_path_last
        
        self._environ_backup = {}
        for n, v in self.environ.items():
            self._environ_backup[n] = os.environ.get(n)
            os.environ[n] = v

        # This code is specific to Nipype/SPM and should be
        # in a dedicated module. It is put here until 
        # modularity is added to this method.
        if os.environ.get('SPM_STANDALONE'):
            nipype = True
            try:
                from nipype.interfaces import spm
            except ImportError:
                nipype = False
            if nipype:
                import glob
                spm_directory = os.environ.get('SPM_DIRECTORY', '')
                spm_exec_glob = osp.join(spm_directory, 'mcr', 'v*')
                spm_exec = glob.glob(spm_exec_glob)
                if spm_exec:
                    spm_exec = spm_exec[0]
                    spm.SPMCommand.set_mlab_paths(
                        matlab_cmd=osp.join(spm_directory, 'run_spm%s.sh' % os.environ.get('SPM_VERSION','')) + ' ' + spm_exec + ' script',
                        use_mcr=True)

    def __exit__(self, exc_type, exc_value, traceback):
        sys_path_error = False
        if self._sys_path_first:
            if sys.path[0:len(self._sys_path_first)] == self._sys_path_first:
                del sys.path[0:len(self._sys_path_first)]
            else:
                sys_path_error = True
        if self._sys_path_last:
            if sys.path[-len(self._sys_path_last):] == self._sys_path_last:
                del sys.path[0:len(self._sys_path_last)]
            else:
                sys_path_error = True
        del self._sys_path_first
        del self._sys_path_last
        
        for n, v in self._environ_backup.items():
            if v is None:
                os.environ.pop(n, None)
            else:
                os.environ[n] = v
        del self._environ_backup
        
        if sys_path_error:
            raise ValueError('sys.path was modified and execution context modifications cannot be undone')
class DummySplashScreen(HasTraits):
    text = Unicode('original')
class ApplicationWindow(MApplicationWindow, Window):
    """ The toolkit specific implementation of an ApplicationWindow.  See the
    IApplicationWindow interface for the API documentation.
    """

    #### 'IApplicationWindow' interface #######################################

    #: The icon to display in the application window title bar.
    icon = Instance(ImageResource)

    #: The menu bar manager for the window.
    menu_bar_manager = Instance(MenuBarManager)

    #: The status bar manager for the window.
    status_bar_manager = Instance(StatusBarManager)

    #: DEPRECATED: The tool bar manager for the window.
    tool_bar_manager = Instance(ToolBarManager)

    #: The collection of tool bar managers for the window.
    tool_bar_managers = List(ToolBarManager)

    #### 'IWindow' interface ##################################################

    #: The window title.
    title = Unicode("Pyface")

    ###########################################################################
    # Protected 'IApplicationWindow' interface.
    ###########################################################################

    def _create_contents(self, parent):
        panel = QtGui.QWidget(parent)

        palette = QtGui.QPalette(panel.palette())
        palette.setColor(QtGui.QPalette.Window, QtGui.QColor('blue'))
        panel.setPalette(palette)
        panel.setAutoFillBackground(True)

        return panel

    def _create_menu_bar(self, parent):
        if self.menu_bar_manager is not None:
            menu_bar = self.menu_bar_manager.create_menu_bar(parent)
            self.control.setMenuBar(menu_bar)

    def _create_status_bar(self, parent):
        if self.status_bar_manager is not None:
            status_bar = self.status_bar_manager.create_status_bar(parent)

            # QMainWindow automatically makes the status bar visible, but we
            # have delegated this responsibility to the status bar manager.
            self.control.setStatusBar(status_bar)
            status_bar.setVisible(self.status_bar_manager.visible)

    def _create_tool_bar(self, parent):
        tool_bar_managers = self._get_tool_bar_managers()
        visible = self.control.isVisible()
        for tool_bar_manager in tool_bar_managers:
            # Add the tool bar and make sure it is visible.
            tool_bar = tool_bar_manager.create_tool_bar(parent)
            self.control.addToolBar(tool_bar)
            tool_bar.show()

            # Make sure that the tool bar has a name so its state can be saved.
            if len(tool_bar.objectName()) == 0:
                tool_bar.setObjectName(tool_bar_manager.name)

        if sys.platform == 'darwin':
            # Work around bug in Qt on OS X where creating a tool bar with a
            # QMainWindow parent hides the window. See
            # http://bugreports.qt.nokia.com/browse/QTBUG-5069 for more info.
            self.control.setVisible(visible)

    def _set_window_icon(self):
        if self.icon is None:
            icon = ImageResource('application.png')
        else:
            icon = self.icon
        if self.control is not None:
            self.control.setWindowIcon(icon.create_icon())

    ###########################################################################
    # 'Window' interface.
    ###########################################################################

    def _size_default(self):
        """ Trait initialiser. """

        return (800, 600)

    ###########################################################################
    # Protected 'IWidget' interface.
    ###########################################################################

    def _create(self):
        super(ApplicationWindow, self)._create()

        contents = self._create_contents(self.control)
        self.control.setCentralWidget(contents)

        self._create_trim_widgets(self.control)

    def _create_control(self, parent):
        control = super(ApplicationWindow, self)._create_control(parent)
        control.setObjectName('ApplicationWindow')

        control.setAnimated(False)
        control.setDockNestingEnabled(True)

        return control

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _get_tool_bar_managers(self):
        """ Return all tool bar managers specified for the window. """

        # fixme: V3 remove the old-style single toolbar option!
        if self.tool_bar_manager is not None:
            tool_bar_managers = [self.tool_bar_manager]

        else:
            tool_bar_managers = self.tool_bar_managers

        return tool_bar_managers

    #### Trait change handlers ################################################

    # QMainWindow takes ownership of the menu bar and the status bar upon
    # assignment. For this reason, it is unnecessary to delete the old controls
    # in the following two handlers.

    def _menu_bar_manager_changed(self):
        if self.control is not None:
            self._create_menu_bar(self.control)

    def _status_bar_manager_changed(self, old, new):
        if self.control is not None:
            if old is not None:
                old.destroy_status_bar()
            self._create_status_bar(self.control)

    @on_trait_change('tool_bar_manager, tool_bar_managers')
    def _update_tool_bar_managers(self):
        if self.control is not None:
            # Remove the old toolbars.
            for child in self.control.children():
                if isinstance(child, QtGui.QToolBar):
                    self.control.removeToolBar(child)
                    child.deleteLater()

            # Add the new toolbars.
            self._create_tool_bar(self.control)

    def _icon_changed(self):
        self._set_window_icon()
示例#19
0
class Wizard(MWizard, Dialog):
    """ The base class for all pyface wizards.

    See the IWizard interface for the API documentation.

    """

    implements(IWizard)

    #### 'IWizard' interface ##################################################

    pages = Property(List(IWizardPage))

    controller = Instance(IWizardController)

    show_cancel = Bool(True)

    #### 'IWindow' interface ##################################################

    title = Unicode('Wizard')

    ###########################################################################
    # Protected 'IDialog' interface.
    ###########################################################################

    def _create_dialog_area(self, parent):
        """ Creates the main content of the dialog. """

        self._layered_panel = panel = LayeredPanel(parent)
        # fixme: Specific size?
        panel.control.SetSize((100, 200))

        return panel.control

    def _create_buttons(self, parent):
        """ Creates the buttons. """

        sizer = wx.BoxSizer(wx.HORIZONTAL)

        # 'Back' button.
        self._back = back = wx.Button(parent, -1, "Back")
        wx.EVT_BUTTON(parent, back.GetId(), self._on_back)
        sizer.Add(back, 0)

        # 'Next' button.
        self._next = next = wx.Button(parent, -1, "Next")
        wx.EVT_BUTTON(parent, next.GetId(), self._on_next)
        sizer.Add(next, 0, wx.LEFT, 5)
        next.SetDefault()

        # 'Finish' button.
        self._finish = finish = wx.Button(parent, wx.ID_OK, "Finish")
        finish.Enable(self.controller.complete)
        wx.EVT_BUTTON(parent, wx.ID_OK, self._wx_on_ok)
        sizer.Add(finish, 0, wx.LEFT, 5)

        # 'Cancel' button.
        if self.show_cancel:
            self._cancel = cancel = wx.Button(parent, wx.ID_CANCEL, "Cancel")
            wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel)
            sizer.Add(cancel, 0, wx.LEFT, 10)

        # 'Help' button.
        if len(self.help_id) > 0:
            help = wx.Button(parent, wx.ID_HELP, "Help")
            wx.EVT_BUTTON(parent, wx.ID_HELP, self._wx_on_help)
            sizer.Add(help, 0, wx.LEFT, 10)

        return sizer

    ###########################################################################
    # Protected 'MWizard' interface.
    ###########################################################################

    def _show_page(self, page):
        """ Show the specified page. """

        panel = self._layered_panel

        # If the page has not yet been shown then create it.
        if not panel.has_layer(page.id):
            panel.add_layer(page.id, page.create_page(panel.control))

        # Show the page.
        layer = panel.show_layer(page.id)
        layer.SetFocus()

        # Set the current page in the controller.
        #
        # fixme: Shouldn't this interface be reversed?  Maybe calling
        # 'next_page' on the controller should cause it to set its own current
        # page?
        self.controller.current_page = page

        return

    def _update(self):
        """ Enables/disables buttons depending on the state of the wizard. """

        controller = self.controller
        current_page = controller.current_page

        is_first_page = controller.is_first_page(current_page)
        is_last_page = controller.is_last_page(current_page)

        # 'Next button'.
        if self._next is not None:
            self._next.Enable(current_page.complete and not is_last_page)

        # 'Back' button.
        if self._back is not None:
            self._back.Enable(not is_first_page)

        # 'Finish' button.
        if self._finish is not None:
            self._finish.Enable(controller.complete)

        # If this is the last page then the 'Finish' button is the default
        # button, otherwise the 'Next' button is the default button.
        if is_last_page:
            if self._finish is not None:
                self._finish.SetDefault()

        else:
            if self._next is not None:
                self._next.SetDefault()

        return

    #### Trait handlers #######################################################

    def _controller_default(self):
        """ Provide a default controller. """

        from pyface.wizard.wizard_controller import WizardController

        return WizardController()

    def _get_pages(self):
        """ Returns the pages in the wizard. """

        return self.controller.pages

    def _set_pages(self, pages):
        """ Sets the pages in the wizard. """

        self.controller.pages = pages

    #### wx event handlers ####################################################

    def _on_next(self, event):
        """ Called when the 'Next' button is pressed. """

        self.next()

        return

    def _on_back(self, event):
        """ Called when the 'Back' button is pressed. """

        self.previous()

        return
示例#20
0
class Dialog(MDialog, Window):
    """ The toolkit specific implementation of a Dialog.  See the IDialog
    interface for the API documentation.
    """

    implements(IDialog)

    #### 'IDialog' interface ##################################################

    cancel_label = Unicode

    help_id = Str

    help_label = Unicode

    ok_label = Unicode

    resizeable = Bool(True)

    return_code = Int(OK)

    style = Enum('modal', 'nonmodal')

    #### 'IWindow' interface ##################################################

    title = Unicode("Dialog")

    ###########################################################################
    # Protected 'IDialog' interface.
    ###########################################################################

    def _create_buttons(self, parent):
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        # The 'OK' button.
        if self.ok_label:
            label = self.ok_label
        else:
            label = "OK"

        self._wx_ok = ok = wx.Button(parent, wx.ID_OK, label)
        ok.SetDefault()
        wx.EVT_BUTTON(parent, wx.ID_OK, self._wx_on_ok)
        sizer.Add(ok)

        # The 'Cancel' button.
        if self.cancel_label:
            label = self.cancel_label
        else:
            label = "Cancel"

        self._wx_cancel = cancel = wx.Button(parent, wx.ID_CANCEL, label)
        wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel)
        sizer.Add(cancel, 0, wx.LEFT, 10)

        # The 'Help' button.
        if len(self.help_id) > 0:
            if self.help_label:
                label = self.help_label
            else:
                label = "Help"

            help = wx.Button(parent, wx.ID_HELP, label)
            wx.EVT_BUTTON(parent, wx.ID_HELP, self._wx_on_help)
            sizer.Add(help, 0, wx.LEFT, 10)

        return sizer

    def _create_contents(self, parent):
        sizer = wx.BoxSizer(wx.VERTICAL)
        parent.SetSizer(sizer)
        parent.SetAutoLayout(True)

        # The 'guts' of the dialog.
        dialog_area = self._create_dialog_area(parent)
        sizer.Add(dialog_area, 1, wx.EXPAND | wx.ALL, 5)

        # The buttons.
        buttons = self._create_buttons(parent)
        sizer.Add(buttons, 0, wx.ALIGN_RIGHT | wx.ALL, 5)

        # Resize the dialog to match the sizer's minimal size.
        if self.size != (-1, -1):
            parent.SetSize(self.size)
        else:
            sizer.Fit(parent)

        parent.CentreOnParent()

    def _create_dialog_area(self, parent):
        panel = wx.Panel(parent, -1)
        panel.SetBackgroundColour("red")
        panel.SetSize((100, 200))

        return panel

    def _show_modal(self):
        if sys.platform == 'darwin':
            # Calling Show(False) is needed on the Mac for the modal dialog
            # to show up at all.
            self.control.Show(False)
        return _RESULT_MAP[self.control.ShowModal()]

    ###########################################################################
    # Protected 'IWidget' interface.
    ###########################################################################

    def _create_control(self, parent):
        style = wx.DEFAULT_DIALOG_STYLE | wx.CLIP_CHILDREN

        if self.resizeable:
            style |= wx.RESIZE_BORDER

        return wx.Dialog(parent, -1, self.title, style=style)

    #### wx event handlers ####################################################

    def _wx_on_ok(self, event):
        """ Called when the 'OK' button is pressed. """

        self.return_code = OK

        # Let the default handler close the dialog appropriately.
        event.Skip()

    def _wx_on_cancel(self, event):
        """ Called when the 'Cancel' button is pressed. """

        self.return_code = CANCEL

        # Let the default handler close the dialog appropriately.
        event.Skip()

    def _wx_on_help(self, event):
        """ Called when the 'Help' button is pressed. """

        print 'Heeeeelllllllllllllpppppppppppppppppppp'
示例#21
0
class ByteEditor(FrameworkEditor):
    """ The toolkit specific implementation of a ByteEditor.  See the
    IByteEditor interface for the API documentation.
    """

    #### 'IPythonEditor' interface ############################################

    obj = Instance(File)

    #### traits

    task_arguments = Str

    grid_range_selected = Bool

    emulator_label = Unicode("Run Emulator")

    #    segment_parser_label = Property(Unicode)
    segment_parser_label = Unicode("whatever")

    initial_font_segment = Any(None)

    ### View traits

    can_copy_baseline = Bool

    can_trace = Bool(False)

    can_resize_document = Bool(False)

    has_origin = Bool(False)

    center_base = Instance(LinkedBase)

    focused_viewer = Any(
        None
    )  # should be Instance(SegmentViewer), but creates circular imports

    linked_bases = List(LinkedBase)

    viewers = List(Any)

    #### Events ####

    changed = Event

    focused_viewer_changed_event = Event

    key_pressed = Event(KeyPressedEvent)

    # Class attributes (not traits)

    rect_select = False

    pane_creation_count = 0

    default_viewers = "hex,bitmap,char,disasm"

    #### trait default values

    def _focused_viewer_default(self):
        return DummyFocusedViewer()

    #### trait property getters

    def _get_segment_parser_label(self):
        return self.document.segment_parser.menu_name if self.document is not None else "<parser type>"

    # Convenience functions

    @property
    def segment(self):
        return self.focused_viewer.linked_base.segment

    @property
    def linked_base(self):
        return self.focused_viewer.linked_base

    @property
    def segment_number(self):
        return self.focused_viewer.linked_base.segment_number

    @property
    def section_name(self):
        return str(self.segment)

    #### Traits event handlers

    def prepare_for_destroy(self):
        self.focused_viewer = None
        # Operate on copy of list because you can't iterate and remove from the
        # same list
        for v in list(self.viewers):
            log.debug(f"Closing viewer: {v}")
            self.viewers.remove(v)
            v.prepare_for_destroy()
            del v
        for b in list(self.linked_bases):
            log.debug(f"Closing linked_base: {b}")
            b.prepare_for_destroy()
            del b

    ###########################################################################
    # 'FrameworkEditor' interface.
    ###########################################################################

    def create(self, parent):
        Machine.one_time_init(self)
        self.control = self._create_control(parent)

    def get_default_layout(self):
        template_name = self.document.calc_layout_template_name(self.task.id)
        log.debug("template from: %s" % template_name)
        data = get_template(template_name)
        try:
            e = json.loads(data)
        except ValueError:
            log.error("invalid data in default layout")
            e = {}
        return e

    def from_metadata_dict(self, e):
        log.debug("metadata: %s" % str(e))
        if 'diff highlight' in e:
            self.diff_highlight = bool(e['diff highlight'])

        viewers = e.get('viewers', [])
        log.debug("metadata: viewers=%s" % str(viewers))

        if not viewers:
            try:
                e_default = self.get_default_layout()
                print(("using defaults from template: template=%s" %
                       str(e_default)))
            except OSError:
                log.error(
                    "No template for default layout; falling back to minimal setup."
                )
            else:
                e.update(e_default)
                viewers = e.get('viewers', [])
            log.debug("from layout: viewers=%s" % str(viewers))

        layout = e.get('layout', {})
        log.debug("metadata: layout=%s" % str(layout))

        viewer_metadata = {}
        for v in viewers:
            viewer_metadata[v['uuid']] = v
            log.debug("metadata: viewer[%s]=%s" % (v['uuid'], str(v)))

        log.debug("task arguments: '%s'" % self.task_arguments)
        if self.task_arguments or not viewer_metadata:
            names = self.task_arguments if self.task_arguments else self.default_viewers
            log.debug("overriding viewers: %s" % str(names))
            override_viewer_metadata = {}
            for viewer_name in names.split(","):
                if viewer_name == "emulator":
                    continue
                override_viewer_metadata[viewer_name.strip()] = {}
                log.debug(
                    "metadata: clearing viewer[%s] because specified in task args"
                    % (viewer_name.strip()))
            if override_viewer_metadata:
                # found some specified viewers, so override the default layout
                viewer_metadata = override_viewer_metadata
                layout = {
                }  # empty layout so it isn't cluttered with unused windows

        linked_bases = {}
        for b in e.get('linked bases', []):
            base = LinkedBase(editor=self)
            base.from_metadata_dict(b)
            linked_bases[base.uuid] = base
            log.debug("metadata: linked_base[%s]=%s" % (base.uuid, base))
        uuid = e.get("center_base", None)
        try:
            self.center_base = linked_bases[uuid]
        except KeyError:
            self.center_base = LinkedBase(editor=self)
        self.create_viewers(layout, viewer_metadata, e, linked_bases)
        viewer = None
        if 'focused viewer' in e:
            u = e['focused viewer']
            viewer = self.find_viewer_by_uuid(u)
        if viewer is None:
            for viewer in self.viewers:
                if not self.control.in_sidebar(viewer.control):
                    break
        print(("setting focus to %s" % viewer))
        self.set_focused_viewer(viewer)
        self.task.segments_changed = self.document.segments

    def to_metadata_dict(self, mdict, document):
        self.prepare_metadata_for_save()
        mdict["diff highlight"] = self.diff_highlight
        mdict["layout"] = self.control.calc_layout()
        mdict["viewers"] = []
        bases = {}
        for v in self.viewers:
            b = v.linked_base
            bases[b.uuid] = b
            e = {"linked base": v.linked_base.uuid}
            v.to_metadata_dict(e, document)
            mdict["viewers"].append(e)
        if self.center_base is not None:
            bases[self.center_base.uuid] = self.center_base
            mdict["center_base"] = self.center_base.uuid
        else:
            mdict["center_base"] = None
        mdict["linked bases"] = []
        for u, b in bases.items():
            e = {}
            b.to_metadata_dict(e, document)
            mdict["linked bases"].append(e)
        mdict["focused viewer"] = self.focused_viewer.uuid
        # if document == self.document:
        #     # If we're saving the document currently displayed, save the
        #     # display parameters too.
        #     mdict["segment view params"] = dict(self.segment_view_params)  # shallow copy, but only need to get rid of Traits dict wrapper

    def prepare_metadata_for_save(self):
        pass

    def rebuild_document_properties(self):
        if not self.document.has_baseline:
            self.use_self_as_baseline(self.document)
        FrameworkEditor.rebuild_document_properties(self)
        b = self.focused_viewer.linked_base
        if b.segment_number == 0:
            self.document.find_initial_visible_segment(b)
        log.debug("rebuilding document %s; initial segment=%s" %
                  (str(self.document), b.segment))
        self.compare_to_baseline()
        self.can_resize_document = self.document.can_resize

    def init_view_properties(self):
        wx.CallAfter(self.force_focus, self.focused_viewer)
        self.task.machine_menu_changed = self.focused_viewer.machine
        # if self.initial_font_segment:
        #     self.focused_viewer.linked_base.machine.change_font_data(self.initial_font_segment)

    def process_preference_change(self, prefs):
        log.debug("%s processing preferences change" % self.task.name)
        #self.machine.set_text_font(prefs.text_font)

    ##### Copy/paste

    @property
    def clipboard_data_format(self):
        return self.focused_viewer.clipboard_data_format

    def copy_selection_to_clipboard(self, name):
        return clipboard.set_from_selection(self.focused_viewer, name)

    def get_paste_data_from_clipboard(self):
        return clipboard.get_paste_data(self.focused_viewer)

    def process_paste_data(self,
                           serialized_data,
                           cmd_cls=None,
                           *args,
                           **kwargs):
        if cmd_cls is None:
            cmd = self.focused_viewer.get_paste_command(
                serialized_data, *args, **kwargs)
        else:
            cmd = cmd_cls(self.segment, serialized_data, *args, **kwargs)
        log.debug("processing paste object %s" % cmd)
        self.process_command(cmd)
        return cmd

    @property
    def supported_clipboard_data_objects(self):
        return self.focused_viewer.supported_clipboard_data_objects

    def select_all(self):
        self.focused_viewer.select_all()
        self.linked_base.refresh_event = True

    def select_none(self):
        self.focused_viewer.select_none()
        self.linked_base.refresh_event = True

    def select_invert(self):
        self.focused_viewer.select_invert()
        self.linked_base.refresh_event = True

    def check_document_change(self):
        self.document.change_count += 1
        self.update_caret_history()

    def rebuild_ui(self):
        log.debug("rebuilding focused_base: %s" %
                  str(self.focused_viewer.linked_base))
        self.document.recalc_event = True

    def refresh_panes(self):
        log.debug("refresh_panes called")

    def reconfigure_panes(self):
        self.update_pane_names()

    def update_pane_names(self):
        for viewer in self.viewers:
            viewer.update_caption()
        self.control.update_captions()

    def view_segment_number(self, number):
        base = self.focused_viewer.linked_base
        base.view_segment_number(number)
        self.update_pane_names()

    def get_extra_segment_savers(self, segment):
        savers = []
        for v in self.viewers:
            savers.extend(v.get_extra_segment_savers(segment))
        return savers

    def save_segment(self, saver, uri):
        try:
            byte_values = saver.encode_data(self.segment, self)
            saver = lambda a, b: byte_values
            self.document.save_to_uri(uri, self, saver, save_metadata=False)
        except Exception as e:
            log.error("%s: %s" % (uri, str(e)))
            #self.window.error("Error trying to save:\n\n%s\n\n%s" % (uri, str(e)), "File Save Error")
            raise

    def show_trace(self):
        """Highlight the current trace after switching to a new segment

        """
        if self.can_trace:
            self.disassembly.update_trace_in_segment()
            self.document.change_count += 1

    ##### Search

    def invalidate_search(self):
        self.task.change_minibuffer_editor(self)

    @property
    def searchers(self):
        search_order = []
        found = set()
        for v in self.viewers:
            for s in v.searchers:
                # searchers may depend on the viewer (like the disassembly)
                # or they may be generic to the segment
                if s.pretty_name not in found:
                    search_order.append(s)
                    found.add(s.pretty_name)
        log.debug("search order: %s" % [s.pretty_name for s in search_order])
        return search_order

    def compare_to_baseline(self):
        if self.diff_highlight and self.document.has_baseline:
            self.document.update_baseline()

    def add_user_segment(self, segment, update=True):
        self.document.add_user_segment(segment)
        self.added_segment(segment, update)

    def added_segment(self, segment, update=True):
        if update:
            self.update_segments_ui()
        self.metadata_dirty = True

    def delete_user_segment(self, segment):
        self.document.delete_user_segment(segment)
        self.view_segment_number(self.segment_number)
        self.update_segments_ui()
        self.metadata_dirty = True

    def update_segments_ui(self):
        # Note: via profiling, it turns out that this is a very heavyweight
        # call, producing hundreds of thousands of trait notifier events. This
        # should only be called when the number of segments or document has
        # changed. If only the segment being viewed is changed, just set the
        # task.segment_selected trait
        log.debug("update_segments_ui costs a lot of time!!!!!!")
        if self.focused_viewer.linked_base.segment_parser is not None:
            self.segment_parser_label = self.focused_viewer.linked_base.segment_parser.menu_name
        else:
            self.segment_parser_label = "No parser"
        self.task.segments_changed = self.document.segments
        self.focused_viewer.linked_base.segment_selected_event = self.segment_number

    def find_in_user_segment(self, base_index):
        # FIXME: Profiling shows this as a big bottleneck when there are
        # comments. It inefficiently loops over segments, then the call to
        # get_index_from_base is super slow in atrcopy because of all the
        # calculations and dereferences needed to compute the index. That
        # probably needs to be cached.
        for s in self.document.user_segments:
            try:
                index = s.get_index_from_base_index(base_index)
                return s, index
            except IndexError:
                continue
        for s in self.document.segment_parser.segments[1:]:
            try:
                index = s.get_index_from_base_index(base_index)
                return s, index
            except IndexError:
                continue
        return None, None

    def do_popup(self, control, popup):
        # The popup event may happen on a control that isn't the focused
        # viewer, and the focused_viewer needs to point to that control for
        # actions to work in the correct viewer. The focus needs to be forced
        # to that control, we can't necessarily count on the ActivatePane call
        # to work before the popup.
        self.focused_viewer = control.segment_viewer
        ret = FrameworkEditor.do_popup(self, control, popup)
        wx.CallAfter(self.force_focus, control.segment_viewer)
        return ret

    def change_bytes(self, start, end, byte_values, pretty=None):
        """Convenience function to perform a ChangeBytesCommand
        """
        self.document.change_count += 1
        cmd = CoalescingChangeByteCommand(self.segment, start, end,
                                          byte_values)
        if pretty:
            cmd.pretty_name = pretty
        self.process_command(cmd)

    def add_viewer(self,
                   viewer_or_viewer_cls,
                   linked_base=None,
                   replace_uuid=None):
        if hasattr(viewer_or_viewer_cls, "control"):
            viewer = viewer_or_viewer_cls
        else:
            if linked_base is None:
                if self.focused_viewer is not None:
                    linked_base = self.focused_viewer.linked_base
                else:
                    raise RuntimeError(
                        "Creating a viewer with no linked base and no focused viewer"
                    )
            viewer = viewer_or_viewer_cls.viewer_factory(
                self.control, linked_base)
        print("VIWER", viewer, type(viewer_or_viewer_cls), linked_base)
        print("SEGMENT", linked_base.segment)
        if replace_uuid:
            viewer.uuid = replace_uuid
        self.viewers.append(viewer)
        self.control.add(viewer.control, viewer.uuid)
        viewer.recalc_data_model()
        self.update_pane_names()
        return viewer

    def replace_viewer(self, viewer_to_replace, new_viewer, linked_base):
        # self.viewers.remove(viewer_to_replace)
        # viewer_to_replace.prepare_for_destroy()
        created_viewer = self.add_viewer(new_viewer,
                                         linked_base,
                                         replace_uuid=viewer_to_replace.uuid)
        # self.set_focused_viewer(created_viewer)
        # del viewer_to_replace

    def replace_focused_viewer(self, linked_base):
        viewer_cls = self.focused_viewer.__class__
        self.replace_viewer(self.focused_viewer, viewer_cls, linked_base)

    def verify_center_base_is_used(self):
        if self.center_base is not None:
            for v in self.viewers:
                if v.linked_base == self.center_base:
                    break
            else:
                self.center_base = None
        print(f"Center base is: {self.center_base}")

    def unlink_viewer(self):
        number = self.focused_viewer.linked_base.segment_number
        base = LinkedBase(editor=self)
        base.view_segment_number(number)
        self.replace_focused_viewer(base)
        self.verify_center_base_is_used()

    def link_viewer(self):
        if self.center_base is None:
            self.center_base = self.focused_viewer.linked_base
        else:
            base = self.center_base
            self.replace_focused_viewer(base)

    ###########################################################################
    # Trait handlers.
    ###########################################################################

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _create_control(self, parent):
        """ Creates the toolkit-specific control for the widget. """

        panel = TileManager(parent,
                            toggle_checker=self.check_viewer_center_base)
        panel.Bind(TileManager.EVT_CLIENT_ACTIVATED, self.on_viewer_active)
        panel.Bind(TileManager.EVT_CLIENT_CLOSE, self.on_viewer_close)
        panel.Bind(TileManager.EVT_CLIENT_REPLACE, self.on_viewer_replace)
        panel.Bind(TileManager.EVT_CLIENT_TOGGLE_REQUESTED,
                   self.on_viewer_link)

        return panel

    def check_viewer_center_base(self, viewer_control, toggle_id):
        try:
            v = viewer_control.segment_viewer
        except AttributeError:
            state = True
        else:
            state = v.linked_base == self.center_base
        return state

    def create_viewers(self, layout, viewer_metadata, default_viewer_metadata,
                       linked_bases):
        # Create a set of viewers from a list
        log.debug("layout: %s" % layout)
        import pprint
        log.debug("viewer_metadata: %s" % str(list(viewer_metadata.keys())))

        self.document.find_initial_visible_segment(self.center_base)

        layer = 0
        viewers = list(viewer_metadata.keys())
        if layout:
            self.control.restore_layout(layout)

        center_base_used = False
        while not self.viewers:
            for uuid in viewers:
                log.debug("loading viewer: %s" % uuid)
                e = viewer_metadata[uuid]
                if e:
                    viewer_type = e['name']
                else:  # either not a uuid or an unknown uuid
                    viewer_type = uuid  # try the value of 'uuid' as a viewer name
                try:
                    viewer_cls = self.task.find_viewer_by_name(viewer_type)
                except ValueError:
                    log.error("unknown viewer %s, uuid=%s" %
                              (viewer_type, uuid))
                    continue
                log.debug("identified viewer: %s" % viewer_cls)

                if e:
                    try:
                        linked_base = linked_bases[e['linked base']]
                    except KeyError:
                        linked_base = self.center_base
                    log.debug("recreating viewer %s: %s" % (viewer_type, uuid))
                else:  # either not a uuid or an unknown uuid
                    linked_base = viewer_cls.calc_segment_specific_linked_base(
                        self)
                    if linked_base is None:
                        linked_base = self.center_base
                    log.debug("using default metadata for %s" % (viewer_type))
                if linked_base == self.center_base:
                    center_base_used = True

                log.debug("creating viewer %s (%s) with linked base %s" %
                          (uuid, viewer_type, str(linked_base)))
                viewer = viewer_cls.viewer_factory(self.control, linked_base,
                                                   None, uuid, e)
                log.debug("created viewer %s (%s)" %
                          (viewer.uuid, viewer.name))

                self.viewers.append(viewer)
                if not self.control.replace_by_uuid(viewer.control,
                                                    viewer.uuid):
                    log.debug("viewer %s not found, adding in new pane" %
                              viewer.uuid)
                    self.control.add(viewer.control, viewer.uuid)

            if not self.viewers:
                # just load default hex editor if nothing has been created
                viewers = ['hex']
                first = False

        if not center_base_used:
            self.center_base = None
        self.update_pane_names()

    def find_viewer_by_uuid(self, u):
        for v in self.viewers:
            if u == v.uuid:
                return v
        return None

    #### wx event handlers

    def force_focus(self, viewer):
        self.control.force_focus(viewer.uuid)
        self.update_pane_names()
        viewer.update_toolbar()

    def set_focused_viewer(self, viewer):
        self.focused_viewer = viewer
        self.focused_viewer_changed_event = viewer
        self.caret_handler = viewer.linked_base
        viewer.linked_base.calc_action_enabled_flags()
        viewer.linked_base.segment_selected_event = viewer.linked_base.segment_number

    def on_viewer_active(self, evt):
        try:
            v = evt.child.segment_viewer
        except AttributeError:
            # must be an empty window (a multisash window that has no segment
            # viewer). It can be closed without any further action.
            pass
        else:
            v = evt.child.segment_viewer
            if v == self.focused_viewer:
                log.debug("on_pane_active: already current viewer %s" % v)
            else:
                log.debug("on_pane_active: activated viewer %s %s" %
                          (v, v.window_title))
                self.set_focused_viewer(v)

    def on_viewer_close(self, evt):
        try:
            v = evt.child.segment_viewer
        except AttributeError:
            # must be an empty window (a multisash window that has no segment
            # viewer). It can be closed without any further action.
            pass
        else:
            log.debug("on_pane_close: closed viewer %s %s" %
                      (v, v.window_title))

            # Keep a reference to the linked base
            linked_base_save = v.linked_base

            self.viewers.remove(v)
            v.prepare_for_destroy()

            from .. import viewers as omnivore8bit_viewers
            if not self.viewers:
                v = self.add_viewer(omnivore8bit_viewers.PlaceholderViewer,
                                    linked_base_save)
            self.set_focused_viewer(self.viewers[0])
            del v

    def on_viewer_replace(self, evt):
        try:
            v = evt.child.segment_viewer
        except AttributeError:
            # must be an empty window (a multisash window that has no segment
            # viewer). It can be closed without any further action.
            pass
        else:
            log.debug(
                "on_viewer_replace: closing viewer %s %s for replacement" %
                (v, v.window_title))

            # Keep a reference to the linked base
            linked_base_save = v.linked_base

            self.viewers.remove(v)
            v.prepare_for_destroy()
            self.set_focused_viewer(evt.replacement_child.segment_viewer)
            del v

    def on_viewer_link(self, evt):
        try:
            v = evt.child.segment_viewer
        except AttributeError:
            # must be an empty window (a multisash window that has no segment
            # viewer). It can be closed without any further action.
            pass
        else:
            toggle = evt.GetInt()
            desired_state = evt.IsChecked()
            log.debug("on_viewer_replace: linking viewer %s %s: %s" %
                      (v, v.window_title, desired_state))
            if desired_state:
                print("LINKED!")
                self.link_viewer()
            else:
                print("UNLINKED!")
                self.unlink_viewer()
示例#22
0
class TasksApplication(Application):
    """The entry point for an Envisage Tasks application.

    This class handles the common case for Tasks applications and is
    intended to be subclassed to modify its start/stop behavior, etc.

    """

    # Extension point IDs.
    TASK_FACTORIES = 'envisage.ui.tasks.tasks'
    TASK_EXTENSIONS = 'envisage.ui.tasks.task_extensions'

    # Pickle protocol to use for persisting layout information. Subclasses may
    # want to increase this, depending on their compatibility needs. Protocol
    # version 2 is safe for Python >= 2.3. Protocol version 4 is safe for
    # Python >= 3.4.
    layout_save_protocol = Int(2)

    #### 'TasksApplication' interface #########################################

    # The active task window (the last one to get focus).
    active_window = Instance('envisage.ui.tasks.task_window.TaskWindow')

    # The Pyface GUI for the application.
    gui = Instance('pyface.gui.GUI')

    # Icon for the whole application. Will be used to override all taskWindows
    # icons to have the same.
    icon = Instance('pyface.image_resource.ImageResource', allow_none=True)

    # The name of the application (also used on window title bars).
    name = Unicode

    # The splash screen for the application. By default, there is no splash
    # screen.
    splash_screen = Instance('pyface.splash_screen.SplashScreen')

    # The directory on the local file system used to persist window layout
    # information.
    state_location = Directory

    # The filename that the application uses to persist window layout
    # information.
    state_filename = Unicode(DEFAULT_STATE_FILENAME)

    # Contributed task factories. This attribute is primarily for run-time
    # inspection; to instantiate a task, use the 'create_task' method.
    task_factories = ExtensionPoint(id=TASK_FACTORIES)

    # Contributed task extensions.
    task_extensions = ExtensionPoint(id=TASK_EXTENSIONS)

    # The list of task windows created by the application.
    windows = List(Instance('envisage.ui.tasks.task_window.TaskWindow'))

    # The factory for creating task windows.
    window_factory = Callable

    #### Application layout ###################################################

    # The default layout for the application. If not specified, a single window
    # will be created with the first available task factory.
    default_layout = List(
        Instance('pyface.tasks.task_window_layout.TaskWindowLayout'))

    # Whether to always apply the default *application level* layout when the
    # application is started. Even if this is True, the layout state of
    # individual tasks will be restored.
    always_use_default_layout = Bool(False)

    #### Application lifecycle events #########################################

    # Fired after the initial windows have been created and the GUI event loop
    # has been started.
    application_initialized = Event

    # Fired immediately before the extant windows are destroyed and the GUI
    # event loop is terminated.
    application_exiting = Event

    # Fired when a task window has been created.
    window_created = Event(
        Instance('envisage.ui.tasks.task_window_event.TaskWindowEvent'))

    # Fired when a task window is opening.
    window_opening = Event(
        Instance(
            'envisage.ui.tasks.task_window_event.VetoableTaskWindowEvent'))

    # Fired when a task window has been opened.
    window_opened = Event(
        Instance('envisage.ui.tasks.task_window_event.TaskWindowEvent'))

    # Fired when a task window is closing.
    window_closing = Event(
        Instance(
            'envisage.ui.tasks.task_window_event.VetoableTaskWindowEvent'))

    # Fired when a task window has been closed.
    window_closed = Event(
        Instance('envisage.ui.tasks.task_window_event.TaskWindowEvent'))

    #### Protected interface ##################################################

    # An 'explicit' exit is when the the 'exit' method is called.
    # An 'implicit' exit is when the user closes the last open window.
    _explicit_exit = Bool(False)

    # Application state.
    _state = Instance(
        'envisage.ui.tasks.tasks_application.TasksApplicationState')

    ###########################################################################
    # 'IApplication' interface.
    ###########################################################################

    def run(self):
        """ Run the application.

        Returns
        -------
        bool
            Whether the application started successfully (i.e., without a
            veto).
        """
        # Make sure the GUI has been created (so that, if required, the splash
        # screen is shown).
        gui = self.gui

        started = self.start()
        if started:
            # Create windows from the default or saved application layout.
            self._create_windows()

            # Start the GUI event loop.
            gui.set_trait_later(self, 'application_initialized', self)
            gui.start_event_loop()

        return started

    ###########################################################################
    # 'TasksApplication' interface.
    ###########################################################################

    def create_task(self, id):
        """ Creates the Task with the specified ID.

        Returns
        -------
        pyface.tasks.task.Task
            The new Task, or None if there is not a suitable TaskFactory.
        """
        # Get the factory for the task.
        factory = self._get_task_factory(id)
        if factory is None:
            return None

        # Create the task using suitable task extensions.
        extensions = [
            ext for ext in self.task_extensions
            if ext.task_id == id or not ext.task_id
        ]
        task = factory.create_with_extensions(extensions)
        task.id = factory.id
        return task

    def create_window(self, layout=None, restore=True, **traits):
        """Creates a new TaskWindow, possibly with some Tasks.

        Parameters
        ----------
        layout : TaskWindowLayout, optional
             The layout to use for the window. The tasks described in
             the layout will be created and added to the window
             automatically. If not specified, the window will contain
             no tasks.

        restore : bool, optional (default True)
             If set, the application will restore old size and
             positions for the window and its panes, if possible. If a
             layout is not provided, this parameter has no effect.

        **traits : dict, optional
             Additional parameters to pass to ``window_factory()``
             when creating the TaskWindow.

        Returns
        -------
        envisage.ui.tasks.task_window.TaskWindow
            The new TaskWindow.

        """
        from .task_window_event import TaskWindowEvent
        from pyface.tasks.task_window_layout import TaskWindowLayout

        window = self.window_factory(application=self, **traits)

        # Listen for the window events.
        window.on_trait_change(self._on_window_activated, 'activated')
        window.on_trait_change(self._on_window_opening, 'opening')
        window.on_trait_change(self._on_window_opened, 'opened')
        window.on_trait_change(self._on_window_closing, 'closing')
        window.on_trait_change(self._on_window_closed, 'closed')

        # Event notification.
        self.window_created = TaskWindowEvent(window=window)

        if layout:
            # Create and add tasks.
            for task_id in layout.get_tasks():
                task = self.create_task(task_id)
                if task:
                    window.add_task(task)
                else:
                    logger.error('Missing factory for task with ID %r',
                                 task_id)

            # Apply a suitable layout.
            if restore:
                layout = self._restore_layout_from_state(layout)
        else:
            # Create an empty layout to set default size and position only
            layout = TaskWindowLayout()

        window.set_window_layout(layout)

        return window

    def exit(self, force=False):
        """Exits the application, closing all open task windows.

        Each window is sent a veto-able closing event. If any window vetoes the
        close request, no window will be closed. Otherwise, all windows will be
        closed and the GUI event loop will terminate.

        This method is not called when the user clicks the close
        button on a window or otherwise closes a window through his or
        her window manager. It is only called via the File->Exit menu
        item. It can also, of course, be called programatically.

        Parameters
        ----------
        force : bool, optional (default False)
            If set, windows will receive no closing events and will be
            destroyed unconditionally. This can be useful for reliably
            tearing down regression tests, but should be used with
            caution.

        Returns
        -------
        bool
            A boolean indicating whether the application exited.

        """
        self._explicit_exit = True
        try:
            if not force:
                for window in reversed(self.windows):
                    window.closing = event = Vetoable()
                    if event.veto:
                        return False

            self._prepare_exit()
            for window in reversed(self.windows):
                window.destroy()
                window.closed = True
        finally:
            self._explicit_exit = False
        return True

    ###########################################################################
    # Protected interface.
    ###########################################################################

    def _create_windows(self):
        """ Called at startup to create TaskWindows from the default or saved
            application layout.
        """
        # Build a list of TaskWindowLayouts.
        self._load_state()
        if (self.always_use_default_layout
                or not self._state.previous_window_layouts):
            window_layouts = self.default_layout
        else:
            # Choose the stored TaskWindowLayouts, but only if all the task IDs
            # are still valid.
            window_layouts = self._state.previous_window_layouts
            for layout in window_layouts:
                for task_id in layout.get_tasks():
                    if not self._get_task_factory(task_id):
                        logger.warning('Saved application layout references '
                                       'non-existent task %r. Falling back to '
                                       'default application layout.' % task_id)
                        window_layouts = self.default_layout
                        break
                else:
                    continue
                break

        # Create a TaskWindow for each TaskWindowLayout.
        for window_layout in window_layouts:
            if self.always_use_default_layout:
                window = self.create_window(window_layout, restore=False)
            else:
                window = self.create_window(window_layout, restore=True)
            window.open()

    def _get_task_factory(self, id):
        """ Returns the TaskFactory with the specified ID, or None.
        """
        for factory in self.task_factories:
            if factory.id == id:
                return factory
        return None

    def _prepare_exit(self):
        """ Called immediately before the extant windows are destroyed and the
            GUI event loop is terminated.
        """
        self.application_exiting = self
        self._save_state()

    def _load_state(self):
        """ Loads saved application state, if possible.
        """
        state = TasksApplicationState()
        filename = os.path.join(self.state_location, self.state_filename)
        if os.path.exists(filename):
            # Attempt to unpickle the saved application state.
            logger.debug('Loading application state from %s', filename)
            try:
                with open(filename, 'rb') as f:
                    restored_state = pickle.load(f)
            except Exception:
                # If anything goes wrong, log the error and continue.
                logger.exception('Error while restoring application state')
            else:
                if state.version == restored_state.version:
                    state = restored_state
                    logger.debug('Application state successfully restored')
                else:
                    logger.warning(
                        'Discarding outdated application state: '
                        'expected version %s, got version %s', state.version,
                        restored_state.version)
        else:
            logger.debug("No saved application state found at %s", filename)

        self._state = state

    def _restore_layout_from_state(self, layout):
        """ Restores an equivalent layout from saved application state.
        """
        # First, see if a window layout matches exactly.
        match = self._state.get_equivalent_window_layout(layout)
        if match:
            # The active task is not part of the equivalency relation, so we
            # ensure that it is correct.
            match.active_task = layout.get_active_task()
            layout = match

        # If that fails, at least try to restore the layout of
        # individual tasks.
        else:
            layout = layout.clone_traits()
            for i, item in enumerate(layout.items):
                id = item if isinstance(item, STRING_BASE_CLASS) else item.id
                match = self._state.get_task_layout(id)
                if match:
                    layout.items[i] = match

        return layout

    def _save_state(self):
        """ Saves the application state.
        """
        # Grab the current window layouts.
        window_layouts = [w.get_window_layout() for w in self.windows]
        self._state.previous_window_layouts = window_layouts

        # Attempt to pickle the application state.
        filename = os.path.join(self.state_location, self.state_filename)
        logger.debug('Saving application state to %s', filename)
        try:
            with open(filename, 'wb') as f:
                pickle.dump(self._state, f, protocol=self.layout_save_protocol)
        except Exception:
            # If anything goes wrong, log the error and continue.
            logger.exception('Error while saving application state')
        else:
            logger.debug('Application state successfully saved')

    #### Trait initializers ###################################################

    def _window_factory_default(self):
        from envisage.ui.tasks.task_window import TaskWindow
        return TaskWindow

    def _default_layout_default(self):
        from pyface.tasks.task_window_layout import TaskWindowLayout
        window_layout = TaskWindowLayout()
        if self.task_factories:
            window_layout.items = [self.task_factories[0].id]
        return [window_layout]

    def _gui_default(self):
        from pyface.gui import GUI
        return GUI(splash_screen=self.splash_screen)

    def _state_location_default(self):
        state_location = os.path.join(ETSConfig.application_home, 'tasks',
                                      ETSConfig.toolkit)
        if not os.path.exists(state_location):
            os.makedirs(state_location)

        logger.debug('Tasks state location is %s', state_location)

        return state_location

    #### Trait change handlers ################################################

    def _on_window_activated(self, window, trait_name, event):
        self.active_window = window

    def _on_window_opening(self, window, trait_name, event):
        from .task_window_event import VetoableTaskWindowEvent
        # Event notification.
        self.window_opening = window_event = VetoableTaskWindowEvent(
            window=window)

        if window_event.veto:
            event.veto = True

    def _on_window_opened(self, window, trait_name, event):
        from .task_window_event import TaskWindowEvent
        self.windows.append(window)

        # Event notification.
        self.window_opened = TaskWindowEvent(window=window)

    def _on_window_closing(self, window, trait_name, event):
        from .task_window_event import VetoableTaskWindowEvent
        # Event notification.
        self.window_closing = window_event = VetoableTaskWindowEvent(
            window=window)

        if window_event.veto:
            event.veto = True
        else:
            # Store the layout of the window.
            window_layout = window.get_window_layout()
            self._state.push_window_layout(window_layout)

            # If we're exiting implicitly and this is the last window, save
            # state, because we won't get another chance.
            if len(self.windows) == 1 and not self._explicit_exit:
                self._prepare_exit()

    def _on_window_closed(self, window, trait_name, event):
        from .task_window_event import TaskWindowEvent
        self.windows.remove(window)

        # Event notification.
        self.window_closed = TaskWindowEvent(window=window)

        # Was this the last window?
        if len(self.windows) == 0:
            self.stop()
示例#23
0
class HeadingText(MHeadingText, Widget):
    """ The toolkit specific implementation of a HeadingText.  See the
    IHeadingText interface for the API documentation.
    """

    implements(IHeadingText)

    #### 'IHeadingText' interface #############################################

    level = Int(1)

    text = Unicode('Default')

    image = Instance(ImageResource, ImageResource('heading_level_1'))

    ###########################################################################
    # 'object' interface.
    ###########################################################################

    def __init__(self, parent, **traits):
        """ Creates the panel. """

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

        # Create the toolkit-specific control that represents the widget.
        self.control = self._create_control(parent)

        return

    ###########################################################################
    # Private interface.
    ###########################################################################

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

        # The background image (it is tiled).
        image = self.image.create_image()
        self._bmp = image.ConvertToBitmap()

        sizer = wx.BoxSizer(wx.VERTICAL)
        panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN | wx.SIMPLE_BORDER)
        panel.SetSizer(sizer)
        panel.SetAutoLayout(True)

        # Create a suitable font.
        self._font = new_font_like(wx.NORMAL_FONT, family=wx.SWISS)

        width, height = self._get_preferred_size(self.text, self._font)
        panel.SetMinSize((width, height))

        wx.EVT_PAINT(panel, self._on_paint_background)
        wx.EVT_ERASE_BACKGROUND(panel, self._on_erase_background)

        return panel

    def _get_preferred_size(self, text, font):
        """ Calculates the preferred size of the widget. """

        dc = wx.ScreenDC()

        dc.SetFont(font)
        width, height = dc.GetTextExtent(text)

        return (width + 10, height + 10)

    def _tile_background_image(self, dc, width, height):
        """ Tiles the background image. """

        w = self._bmp.GetWidth()
        h = self._bmp.GetHeight()

        x = 0
        while x < width:
            y = 0
            while y < height:
                dc.DrawBitmap(self._bmp, x, y)
                y = y + h

            x = x + w

        return

    #### Trait event handlers #################################################

    def _text_changed(self, new):
        """ Called when the text is changed. """

        if self.control is not None:
            self.control.Refresh()

        return

    #### wx event handlers ####################################################

    def _on_paint_background(self, event):
        """ Called when the background of the panel is painted. """

        dc = wx.PaintDC(self.control)
        size = self.control.GetClientSize()

        # Tile the background image.
        self._tile_background_image(dc, size.width, size.height)

        # Render the text.
        dc.SetFont(self._font)
        dc.DrawText(self.text, 5, 4)

        return

    def _on_erase_background(self, event):
        """ Called when the background of the panel is erased. """

        dc = event.GetDC()
        size = self.control.GetClientSize()

        # Tile the background image.
        self._tile_background_image(dc, size.width, size.height)

        # Render the text.
        dc.SetFont(self._font)
        dc.DrawText(self.text, 5, 4)

        return
示例#24
0
class FragmentDataSourceModel(BaseDataSourceModel):
    """Class containing all input parameters for a single molecular
    fragment in a Gromacs simulation"""

    # --------------------
    #  Required Attributes
    # --------------------

    #: Name of the fragment
    name = Unicode(desc='Name of fragment')

    #: Reference symbol of fragment in Gromacs files
    symbol = Unicode(desc='Reference symbol in input Gromacs topology file')

    #: Location of Gromacs topology file containing molecular data
    topology = File(desc='File path for Gromacs topology file',
                    exists=True,
                    verify=True)

    #: Location of Gromacs coordinate file containing molecular data
    coordinate = File(desc='File path for Gromacs coordinate file',
                      exists=True,
                      verify=True)

    # --------------------
    #    Private Methods
    # --------------------

    def _file_check(self, file_path, ext=None):
        """Performs a series of checks on selected Gromacs file located
        at file_path

        Parameters
        ----------
        file_path: str
            File path for Gromacs input file
        ext: str, optional
            Expected extension of Gromacs input file
        """

        errors = []
        if not file_path or file_path.isspace():
            errors.append(
                VerifierError(
                    subject=self,
                    local_error="Gromacs file name is white space.",
                    global_error=("Gromacs file not specified."),
                ))

        if ext is not None:
            if not file_path.endswith('.{}'.format(ext)):
                errors.append(
                    VerifierError(
                        subject=self,
                        local_error="File extension does not match required.",
                        global_error=(
                            "File is not a valid Gromacs file type."),
                    ))

        return errors

    # --------------------
    #    Public Methods
    # --------------------

    def verify(self):
        """Overloads BaseDataSourceModel verify method to check file names
        status upon activation of verify_workflow_event."""

        errors = super(FragmentDataSourceModel, self).verify()
        errors += self._file_check(self.topology, 'itp')
        errors += self._file_check(self.coordinate, 'gro')

        return errors
示例#25
0
class ViewMenuManager(MenuManager):
    """ The 'View' menu.

    By default, this menu is displayed on the main menu bar.

    """

    #### 'ActionManager' interface ############################################

    # All of the groups in the manager.
    groups = List(Group)

    # The manager's unique identifier (if it has one).
    id = Str('View')

    #### 'MenuManager' interface ##############################################

    # The menu manager's name (if the manager is a sub-menu, this is what its
    # label will be).
    name = Unicode('&View')

    #### 'ViewMenuManager' interface ##########################################

    # Should the perspective menu be shown?
    show_perspective_menu = Bool(True)

    # The workbench window that the menu is part of.
    window = Instance('pyface.workbench.api.WorkbenchWindow')

    #### 'Private' interface ##################################################

    # The group containing the view hide/show actions.
    _view_group = Any

    ###########################################################################
    # 'ActionManager' interface.
    ###########################################################################

    def _groups_default(self):
        """ Trait initializer. """

        groups = []

        # Add a group containing the perspective menu (if requested).
        if self.show_perspective_menu and len(self.window.perspectives) > 0:
            groups.append(Group(PerspectiveMenuManager(window=self.window)))

        # Add a group containing a 'toggler' for all visible views.
        self._view_group = self._create_view_group(self.window)
        groups.append(self._view_group)

        # Add a group containing an 'Other...' item that will launch a dialog
        # to allow the user to choose a view to show.
        groups.append(self._create_other_group(self.window))

        return groups

    ###########################################################################
    # 'ViewMenuManager' interface.
    ###########################################################################

    @on_trait_change('window.active_perspective,window.active_part,'
                     'window.views,window.views_items')
    def refresh(self):
        """ Refreshes the checked state of the actions in the menu. """

        logger.debug('refreshing view menu')

        if self._view_group is not None:
            self._clear_group(self._view_group)
            self._initialize_view_group(self.window, self._view_group)
            self.changed = True

        return

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _clear_group(self, group):
        """ Remove all items in a group. """

        # fixme: Fix this API in PyFace so there is only one call!
        group.destroy()
        group.clear()

        return

    def _create_other_group(self, window):
        """ Creates a group containing the 'Other...' action. """

        group = Group()
        group.append(ShowViewAction(name='Other...', window=window))

        return group

    def _create_view_group(self, window):
        """ Creates a group containing the view 'togglers'. """

        group = Group()
        self._initialize_view_group(window, group)

        return group

    def _initialize_view_group(self, window, group):
        """ Initializes a group containing the view 'togglers'. """

        views = window.views[:]
        views.sort(None, lambda view: view.name)

        for view in views:
            # fixme: It seems a little smelly to be reaching in to the window
            # layout here. Should the 'contains_view' method be part of the
            # window interface?
            if window.layout.contains_view(view):
                group.append(
                    ToggleViewVisibilityAction(view=view, window=window))

        return
示例#26
0
文件: task.py 项目: jomason/omnivore
class FrameworkTask(Task):
    """ A simple task for opening a blank editor.
    """

    # Class properties (not traits!) because they must be available in a TaskFactory

    new_file_text = ''

    about_application = "about://omnivore"  # URL to load if no document specified on the command line

    editor_id = 'omnivore.framework.framework_task'

    pane_layout_version = ''

    doc_hint = ''  # directives for document formatter

    about_filesystem = {  # extra documents to place in the about:// filesystem
    }

    #### Task interface #######################################################

    id = editor_id + "." + pane_layout_version if pane_layout_version else editor_id
    name = 'Framework'

    icon = Instance(ImageResource)

    active_editor = Property(Instance(IEditor),
                             depends_on='editor_area.active_editor')

    editor_area = Instance(IEditorAreaPane)

    #### FrameworkTask interface ##############################################

    preferences_helper = Instance(FrameworkPreferences)

    status_bar_debug_width = Int(150)

    start_new_editor_in_new_window = Bool(False)

    print_data = Any

    document_changed = Event

    # event that causes the menubar/toolbar to be updated according to
    # the ui state variables
    menu_update_event = Event

    keyboard_shortcuts = Any

    # class attribute

    activated_task_ids = set()

    #### 'IAbout' interface ###################################################

    about_title = Unicode('Omnivore XL')

    about_version = Unicode

    about_description = Unicode(
        'Byte into the meat of 8-bit Software!\n\nSend feedback to: [email protected]'
    )

    about_website = Str('http://playermissile.com/omnivore')

    about_image = Instance(ImageResource, ImageResource('omnivore256'))

    #### 'IErrorReporter' interface ###########################################

    error_email_from = Str

    error_email_passwd = Str

    error_email_to = Str

    # Regular ol' class attributes here

    # description of menus
    ui_layout_description = {
        "menu": {
            "order": ["File", "Edit", "View", "Documents", "Window", "Help"],
            "File": ["NewGroup", "OpenGroup", "ImportGroup", "SaveGroup", "RevertGroup", "PrintGroup", "ExportGroup", "ExitGroup"],
            "Edit": ["UndoGroup", "CopyPasteGroup", "SelectGroup", "FindGroup", "PrefGroup"],
            "View": ["PredefinedGroup", "ZoomGroup", "ChangeGroup", "ConfigGroup", "ToggleGroup", "TaskGroup", "DebugGroup"],
            "Documents": ["DocumentGroup"],
            "Window": ["NewTaskGroup", "WindowGroup"],
            "Help": ["AboutGroup", "DocGroup", "BugReportGroup", "DebugTaskGroup", "DebugFrameworkGroup"],
        },
        "MenuBar": {  # documentation for menus and submenus that don't have actions
            "Documents": "This menu contains a list of all documents open in the current session, across all windows and tabs. Selecting an item in this list will switch the view to that document, using the editor that was being used the last time it was edited.",
            "Window": {
                "New View In...": "This menu will contain a list of all editors that can modify this file. Selecting an item in this list will add a new view into this file using the selected editor.  For instance, you can have a map editor and a hex editor on the same file; they point to the same data and modifying data in one window will show up in the other window.",
            },
        },

        "toolbar": {
            "order": ["File", "Edit", "View"],
            "File": ["NewGroup", "OpenGroup", "SaveGroup"],
            "Edit": ["UndoGroup", "CopyPasteGroup", "SelectGroup", "FindGroup"],
            "View": ["ZoomGroup", "ChangeGroup", "ConfigGroup"],
        },
    }

    # subclasses can make changes by putting entries into this structure.
    # Entries here will replace items in the ui_layout_description dict
    ui_layout_overrides = {}

    # IAbout interface

    def _about_version_default(self):
        from omnivore import __version__
        return __version__

    ###########################################################################
    # 'Task' interface.
    ###########################################################################

    def _icon_default(self):
        return ImageResource('omnivore')

    def get_layout_description(self, category, title):
        try:
            log.debug("%s: %s/%s found in overrides" %
                      (self.name, category, title))
            return self.ui_layout_overrides[category][title]
        except KeyError:
            return self.ui_layout_description[category][title]
        log.error("Ui for %s, %s not found s" % (category, title))

    def get_submenu_docs(self, menu_list):
        """Find any submenu docs from the MenuBar or Tool Bar keys in
        the ui_layout_* dicts.

        The hierarchy of pyface actions is very complicated and I can't figure
        out how to attach documentation to a submenu. So we're left with this
        mess. At least it's all in one spot.
        """
        category = menu_list.pop(0)
        desc = ""
        for ui in [self.ui_layout_overrides, self.ui_layout_description]:
            try:
                desc = ui[category]
                print category, desc
                for m in menu_list:
                    desc = desc[m]
                    print m, desc
            except KeyError:
                desc = ""
                continue
            else:
                break
        print "SUBMENU_DOCS", menu_list, desc
        if not isinstance(desc, basestring):
            desc = ""
        return desc

    def _menu_bar_default(self):
        menus = []
        for menu_title in self.get_layout_description("menu", "order"):
            menu_desc = self.get_layout_description("menu", menu_title)
            self.add_menu(menus, "Menu", menu_title, *menu_desc)
        return SMenuBar(*menus)

    def _tool_bars_default(self):
        bars = []
        desc = self.ui_layout_description["toolbar"]
        for menu_title in desc["order"]:
            menu_desc = desc[menu_title]
            bars.append(self.get_groups("Tool", menu_title, *menu_desc))
        return [
            SToolBar(*bars, show_tool_names=False, id="%s:ToolBar" % self.id)
        ]

    def _status_bar_default(self):
        return FrameworkStatusBarManager(
            message="Hi!", debug_width=self.status_bar_debug_width)

    def _default_layout_default(self):
        return TaskLayout()

    def _keyboard_shortcuts_default(self):
        actions = []

        # Give each keyboard action a wx ID so that it can be identified in the
        # menu callback
        for action in self.get_keyboard_actions():
            id = wx.NewId()
            action.keyboard_shortcut_id = id
            actions.append(action)
        return actions

    ##### Task setup/cleanup

    def initialized(self):
        self.window.application.remember_perspectives(self.window)
        c = self.editor_area.control
        c.Bind(aui.EVT_AUINOTEBOOK_TAB_RIGHT_DOWN, self.on_tab_context_menu)
        c.Bind(aui.EVT_AUINOTEBOOK_BG_RIGHT_DOWN,
               self.on_tab_background_context_menu)
        c.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.on_tab_pane_close)

    def activated(self):
        log.debug("  status bar: %s" % self.status_bar)
        active = self.active_editor
        if active:
            self.status_bar.message = active.name
        else:
            self.status_bar.message = self.name
        self.window.icon = self.icon
        self.set_keyboard_shortcuts()
        self._active_editor_tab_change(None)
        visible = self.pane_layout_initial_visibility()
        for pane in self.iter_panes():
            if pane.id in visible:
                pane.visible = visible[pane.id]

        if self.id not in self.activated_task_ids:
            self.activated_task_ids.add(self.id)
            self.first_time_activated()

    def first_time_activated(self):
        """ Called the first time a task is activated in the application.
        """
        pass

    def pane_layout_initial_visibility(self):
        return {}

    def create_central_pane(self):
        """ Create the central pane: the text editor.
        """
        self.editor_area = EditorAreaPane()
        return self.editor_area

    def create_dock_panes(self):
        """ Create any dock panes and connect to its double click event.
        """
        return []

    def iter_panes(self):
        for pane in self.window.dock_panes:
            yield pane

    def prepare_destroy(self):
        self.window.application.remember_perspectives(self.window)
        self.destroy_minibuffer()

    ###########################################################################
    # 'FrameworkTask' interface.
    ###########################################################################

    def new(self, source=None, **kwargs):
        """ Opens a new tab
        
        :param source: optional :class:`FileGuess` or :class:`Editor` instance
        that will load a new file or create a new view of the existing editor,
        respectively.
        """
        try:
            editor = self.get_editor(**kwargs)
        except e:
            self.error("Failed creating editor: %s" % str(e))
            return
        self.editor_area.add_editor(editor)
        self.editor_area.activate_editor(editor)
        log.debug("new: source=%s editor=%s" % (source, editor))
        if hasattr(source, 'get_metadata') or source is None:
            editor.load(source, **kwargs)
            if source is not None:
                self.window.application.successfully_loaded_event = source.metadata.uri
        elif hasattr(source, 'document_id'):
            source.load_permute(editor)
            editor.load_document(source)
            self.window.application.successfully_loaded_event = source.metadata.uri
        else:
            editor.view_document(source.document, source)
        self.activated()

    def find_tab_or_open(self, document):
        for editor in self.editor_area.editors:
            if editor.document == document:
                self.editor_area.activate_editor(editor)
                return
        self.new(document)

    def new_window(self, task=None, view=None):
        """ Opens a new window
        
        With no arguments, opens an empty window with the default task.
        
        :keyword task: optional task to set the task of the new window
        
        :keyword view: optional :class:`Editor` instance that will set the
        task of the new window and will create a new view of the editor in the
        first tab
        """
        log.debug("Opening new window!!!")
        window = self.window.application.create_window()
        log.debug("  window=%s" % str(window))
        log.debug("  self=%s" % str(self.window))
        log.debug("  task type: %s" % str(task))
        log.debug("  view of: %s" % str(view))
        task_id = None
        if view is not None:
            task_cls = view.editor_area.task.__class__
            log.debug("  task_cls: %s" % str(task_cls))
        elif task is not None:
            if isinstance(task, FrameworkTask):
                task_cls = task.__class__
            else:
                task_cls = task
        else:
            task_id = self.window.application.startup_task
        if task_id is None:
            task = task_cls()
            task_id = task.id
        log.debug("  task id: %s" % task_id)
        log.debug("  returned factory: %s" %
                  self.window.application._get_task_factory(task_id))
        for factory in self.window.application.task_factories:
            log.debug("    factory: %s, %s" % (factory.id, factory))

        task = self.window.application.create_task(task_id)
        log.debug("  created task: %s" % task)
        window.add_task(task)
        window.activate_task(task)
        if view is not None:
            task.new(view)
        window.open()
        log.debug("All windows: %s" % self.window.application.windows)

    def prompt_local_file_dialog(self,
                                 title="",
                                 most_recent=True,
                                 save=False,
                                 default_filename="",
                                 wildcard="*"):
        """Display an "open file" dialog to load from the local filesystem,
        defaulting to the most recently used directory.

        If there is no previously used directory, default to the directory of
        the current file.

        Returns the directory path on the filesystem, or None if not found.
        """
        dirpath = ""
        action = "save as" if save else "open"
        if not title:
            title = "Save File" if save else "Open File"
        if self.active_editor:
            # will try path of current file
            dirpath = self.active_editor.best_file_save_dir

        dialog = FileDialog(parent=self.window.control,
                            title=title,
                            default_directory=dirpath,
                            action=action,
                            default_filename=default_filename,
                            wildcard=wildcard)
        if dialog.open() == OK:
            return dialog.path
        return None

    def save(self):
        """ Attempts to save the current file, prompting for a path if
            necessary. Returns whether the file was saved.
        """
        editor = self.active_editor
        try:
            editor.save()
        except IOError:
            # If you are trying to save to a file that doesn't exist, open up a
            # FileDialog with a 'save as' action.
            path = prompt_local_file_dialog(save=True)
            if path:
                editor.save(path)
            else:
                return False
        return True

    def allow_different_task(self, guess, other_task):
        """Hook to allow tasks to abort loading different task window.
        
        This method allows a hook to confirm that the user wants to load a file
        that can't be handled by the current task.  For example, this can be
        used to prompt with a dialog box.
        
        :rtype: Boolean; True means continue with the file load in a separate
        task window
        """
        return True

    def _print_data_default(self):
        data = wx.PrintData()
        data.SetPaperId(wx.PAPER_LETTER)
        data.SetOrientation(wx.PORTRAIT)
        return data

    def page_setup(self):
        data = wx.PageSetupDialogData(self.print_data)
        data.SetDefaultMinMargins(True)

        dlg = wx.PageSetupDialog(self.window.control, data)

        if dlg.ShowModal() == wx.ID_OK:
            data = dlg.GetPageSetupData().GetPrintData()
            self.print_data = wx.PrintData(data)  # Force a copy

        dlg.Destroy()

    def debug(self):
        """Debug stuff!
        """
        action_schemas = list(self.menu_bar.items)
        action_schemas.extend(self.tool_bars)
        for action in self._iter_schema_items(action_schemas):
            if hasattr(action, 'name'):
                action.name = action.name + "!"

    def ask_attempt_loading_as_octet_stream(self, guess, other_task):
        return self.confirm(
            "%s\n\nwas identified with a MIME type of %s\nand can also be edited in a %s window.\n\nOpen here in the %s window instead?"
            % (guess.metadata.uri, guess.metadata.mime,
               other_task.new_file_text, self.name), "Edit in %s?" % self.name)

    ###########################################################################
    # 'FrameworkTask' convenience functions.
    ###########################################################################

    @property
    def preferences(self):
        return self.window.application.get_preferences(self.preferences_helper)

    def create_menu(self, location, menu_name, *group_names):
        items = []
        for group_name in group_names:
            self.add_actions_and_groups(items, location, menu_name, group_name)
        return SMenu(*items, id=menu_name, name=menu_name)

    def add_menu(self, menu, location, menu_name, *group_names):
        entry = self.create_menu(location, menu_name, *group_names)
        menu.append(entry)

    def add_actions_and_groups(self, menu_items, location, menu_name,
                               group_name):
        actions = self.get_actions_wrapper(location, menu_name, group_name)

        groups = []
        group_suffix = ""
        group_index = 0
        current = []

        for item in actions:
            if isinstance(item, Group) or isinstance(item, SMenu):
                if current:
                    group = Group(*current,
                                  id="%s%s" % (group_name, group_suffix))
                    group_index += 1
                    group_suffix = str(group_index)
                    groups.append(group)
                    current = []
                groups.append(item)
            else:
                current.append(item)
        if current:
            group = Group(*current, id="%s%s" % (group_name, group_suffix))
            groups.append(group)

        menu_items.append(Separator(id="%sStart" % group_name,
                                    separator=False))
        for group in groups:
            menu_items.append(group)
        menu_items.append(Separator(id="%sEnd" % group_name, separator=False))

    def get_group(self, location, menu_name, group_name):
        actions = self.get_actions_wrapper(location, menu_name, group_name)
        return Group(*actions, id=group_name)

    def get_groups(self, location, menu_name, *group_names):
        actions = []
        for group_name in group_names:
            actions.extend(
                self.get_actions_wrapper(location, menu_name, group_name))
        return Group(*actions, id=menu_name)

    def get_actions_wrapper(self, location, menu_name, group_name):
        actions = self.get_actions(location, menu_name, group_name)
        if actions is None:
            # fall back to this class if it's not found in subclass
            actions = FrameworkTask.get_actions(self, location, menu_name,
                                                group_name)
        return actions

    def get_actions(self, location, menu_name, group_name):
        # allow spaces in menu names by removing them for function lookup
        menu_lookup = menu_name.replace(" ", "")
        method_name = "get_actions_%s_%s_%s" % (location, menu_lookup,
                                                group_name)
        try:
            method = getattr(self, method_name)
        except AttributeError, e:
            log.debug("%s actions not found for %s/%s in %s" %
                      (location, menu_name, group_name, self.id))
            actions = []
        else: