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
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
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
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()
class AskName(HasTraits): name = Unicode("") view = View(Item("name", label="名称"), kind="modal", buttons=OKCancelButtons)
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
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()
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)
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)
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)
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)
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
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()
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
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'
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()
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()
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
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
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
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: