def add_item(self, item): """Add menu item. Item will be passed into self.command. :param str item: The item to add. """ maya.menu_option(label=item, parent=self.menu, command=lambda a: self.command(item))
def init_after_account_selected(self): try: if not hasattr(self, "submission"): self.submission = AzureBatchSubmission( self.tab_index['SUBMIT'], self.frame, self.call) if not hasattr(self, "assets"): self.assets = AzureBatchAssets(self.tab_index['ASSETS'], self.frame, self.call) if not hasattr(self, "pools"): self.pools = AzureBatchPools(self.tab_index['POOLS'], self.frame, self.call) if not hasattr(self, "jobhistory"): self.jobhistory = AzureBatchJobHistory( self.tab_index['JOBHISTORY'], self.frame, self.call) if not hasattr(self, "env"): self.env = AzureBatchEnvironment(self.tab_index['ENV'], self.frame, self.call) self.config.auth = True self.start() except Exception as exp: if (maya.window("AzureBatch", q=1, exists=1)): maya.delete_ui("AzureBatch") message = "Batch Plugin Failed to Start: {0}".format(exp) maya.error(message) raise
def call(self, command, *args, **kwargs): """Wrap all Batch and Storage API calls in order to handle errors. Some errors we anticipate and raise without a dialog (e.g. PoolNotFound). Others we raise and display to the user. """ try: result = command(*args, **kwargs) return self.ensure_iter_called(result) except BatchErrorException as exp: #if auth error (401) refresh tokens and recreate clients, before repeating the call if exp.response.status_code in [401]: self.config.refresh_auth_tokens(self.config.batch_auth_token, self.config.mgmt_auth_token) self.config.update_batch_and_storage_client_creds( self.config.batch_auth_token, self.config.mgmt_auth_token) result = command(*args, **kwargs) try: return self.ensure_iter_called(result) except BatchErrorException as exp: self.handle_batch_exception(exp) else: self.handle_batch_exception(exp) except Exception as exp: if (maya.window("AzureBatch", q=1, exists=1)): maya.delete_ui("AzureBatch") exc_type, exc_value, exc_traceback = sys.exc_info() self._log.error("".join( traceback.format_exception(exc_type, exc_value, exc_traceback))) raise ValueError("Error: {0}".format(exp))
def set_thumbnail(self, thumb, height): """Set thumbnail image. :param str thumb: The path to the thumbnail image. :param int height: The height of the image in pixels. """ maya.image(self._thumbnail, edit=True, image=thumb, height=height)
def start(self): """Place the button in a processing state.""" maya.button(self._button, edit=True, enable=False, label=self.prob_label) maya.refresh()
def __init__(self, base, index, layout): """Create a new pool reference. :param base: The base class for handling pools monitoring functionality. :type base: :class:`.AzureBatchPools` :param int index: The index of where this reference is displayed on the current page. :param layout: The layout on which the pool details will be displayed. :type layout: :class:`.utils.FrameLayout` """ self.base = base self.index = index self.layout = layout maya.frame_layout(layout, edit=True, collapseCommand=self.on_collapse, expandCommand=self.on_expand) self.listbox = maya.col_layout(numberOfColumns=2, columnWidth=((1, 100), (2, 200)), rowSpacing=((1, 5), ), rowOffset=( (1, "top", 5), (1, "bottom", 5), ), parent=self.layout) self.content = []
def init_after_subscription_selected(self): self.account_status = "Loading" maya.refresh() if self.batch_account_row is not None: maya.delete_ui(self.batch_account_row) if self.account_ui_elements: maya.delete_ui(self.account_ui_elements) self.disable(False) self.account_ui_elements = [] with utils.Row( 2, 2, (100, 200), ("left", "left"), parent=self.batch_account_framelayout) as batch_account_row: self.batch_account_row = batch_account_row maya.text(label="Batch Account: ", align="right") with utils.Dropdown( self.select_account_in_dropdown) as account_dropdown: self._account_dropdown = account_dropdown self._account_dropdown.add_item("") accounts = self.base.available_batch_accounts() self.accounts_by_name = dict([(account.name, account) for account in accounts]) for account in accounts: self._account_dropdown.add_item(account.name) self.account_status = "Please select Batch Account" self.draw_threads_and_logging_rows(self.frames_layout, self.account_ui_elements) self.disable(True) maya.refresh()
def load_tasks(self): """Get a list of tasks associated with the job.""" try: job = self.jobs[self.selected_job.index] except (IndexError, AttributeError): self._log.warning("Selected job index does not match jobs list.") if not self.selected_job: return self.selected_job.set_status('unknown') self.selected_job.set_progress('unknown') self.selected_job.set_tasks('unknown') return try: tasks = list(self._call(self.batch.task.list, job.id)) completed_tasks = [ t for t in tasks if t.state == batch.models.TaskState.completed ] errored_tasks = [ t for t in completed_tasks if t.execution_info.exit_code != 0 ] state = job.state.value if len(tasks) == 0: percentage = 0 state = "Pending" else: percentage = (100 * len(completed_tasks)) / (len(tasks)) self.selected_job.set_status(state) self.selected_job.set_progress(str(percentage) + '%') self.selected_job.set_tasks(len(tasks)) maya.refresh() except Exception as exp: self._log.warning("Failed to update job details {0}".format(exp)) self.ui.refresh()
def set_marketplace_image_mode(self, *args): """Set selected render node type to be a batch published VM image type. Displays the Batch VM image selection UI control. Command for select_rendernode_type radio buttons. """ self.select_rendernode_type = PoolImageMode.MARKETPLACE_IMAGE.value if self.image_config != None: maya.delete_ui(self.image_config) self.image_config = [] with utils.FrameLayout(label="Batch Provided Image Settings", collapsable=True, width=325, collapse=False, parent=self.rendernode_config) as framelayout: self.image_config.append(framelayout) with utils.ColumnLayout(2, col_width=((1, 80), (2, 160)), row_spacing=(1, 5), row_offset=((1, "top", 15), (5, "bottom", 15)), parent=framelayout) as os_image_layout: self.image_config.append(os_image_layout) maya.text(label="Use Image: ", align='left', parent=os_image_layout) with utils.Dropdown(self.set_os_image, parent=os_image_layout) as image_settings: self._image = image_settings for image in self.batch_images: self._image.add_item(image)
def start(self, session, assets, pools, env): """Load submission tab after plug-in has been authenticated. :param session: Authenticated configuration handler. :type session: :class:`.AzureBatchConfig` :param assets: Asset handler. :type assets: :class:`.AzureBatchAssets` :param pools: Pool handler. :type pools: :class:`.AzureBatchPools` :param env: Render node environment handler. :type env: :class:`.AzureBatchEnvironment` """ self._log.debug("Starting AzureBatchSubmission...") self.batch = session.batch self.asset_manager = assets self.pool_manager = pools self.env_manager = env self.data_path = session.path if self.renderer: self.renderer.delete() self._configure_renderer() self.renderer.display(self.ui.render_module) self.ui.submit_enabled(self.renderer.render_enabled()) self.ui.is_logged_in() maya.refresh()
def update_job(self, index): """Get the latest details on a specified job. Also attempts to find a latest thumbnail to display. :param int index: The index of the job displayed on the page that is currently selected. """ try: self._log.info("Collecting job info...") job = self.jobs[index] self.selected_job.set_label("loading...") loading_thumb = os.path.join(os.environ["AZUREBATCH_ICONS"], "loading_preview.png") self.selected_job.set_thumbnail(loading_thumb, 24) maya.refresh() job = self._call(self.batch.job.get, job.id) self.selected_job.set_status('loading...') self.selected_job.set_progress('loading...') self.selected_job.set_tasks('loading...') self.selected_job.set_submission(job.creation_time.isoformat()) self.selected_job.set_job(job.id) self.selected_job.set_pool(job.pool_info.pool_id) self.selected_job.set_label(job.display_name) maya.refresh() self._log.info("Updated {0}".format(job.display_name)) except Exception as exp: self._log.warning("Failed to update job details {0}".format(exp)) self.ui.refresh()
def refresh(self, *args): """Ensure settings are up to date with the current scene and renderer.""" self.base.refresh() if self.select_rendernode_type == PoolImageMode.CONTAINER_IMAGE.value: self.set_container_image_mode() self.refresh_licenses() maya.refresh()
def _check_plugins(self): """Checks through all plug-ins that are currently in use (according to Maya) by the scene. We compile a list of those that we both support and should not ignore and return for inclusion in the pre-render script plug-in loading. TODO: This has been temporarily disabled because some of the plug-ins were causing errors in Maya on the render nodes. Need to investigate and add the culprits to the ignore list if necessary. """ try: with open( os.path.join(os.environ['AZUREBATCH_TOOLS'], "supported_plugins.json"), 'r') as plugins: supported_plugins = json.load(plugins) with open( os.path.join(os.environ['AZUREBATCH_TOOLS'], "ignored_plugins.json"), 'r') as plugins: ignored_plugins = json.load(plugins) except EnvironmentError: self._log.warning("Unable to load supported plugins") return [] loaded_plugins = maya.plugins(query=True, listPlugins=True) unsupported_plugins = [p for p in loaded_plugins \ if p not in supported_plugins and p not in ignored_plugins] if unsupported_plugins: warning = ( "The following plug-ins are used in the scene, but are not " "yet supported.\nRendering may be affected.\n") for plugin in unsupported_plugins: warning += plugin + "\n" options = ['Continue', 'Cancel'] answer = maya.confirm(warning, options) if answer == options[-1]: raise CancellationException("Submission Aborted") return []
def display(self, ui, layout, scroll): """Create the UI elements that will display this asset depending on whether the file path has been resolved or not. """ self.frame = ui self.scroll_layout = scroll if self.exists: self.check_box = maya.symbol_check_box( value=True, parent=layout, onCommand=lambda e: self.include(), offCommand=lambda e: self.exclude(), annotation="Click to remove asset from submission") else: self.check_box = maya.symbol_button( image="fpe_someBrokenPaths.png", parent=layout, command=lambda e: self.search(), height=17, annotation="Add search path") self.display_text = maya.text(self.label, parent=layout, enable=self.exists, annotation=self.note, align="left")
def __init__(self, base, frame): """Create 'Pools' tab and add to UI frame. :param base: The base class for handling pools monitoring functionality. :type base: :class:`.AzureBatchPools` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.label = "Pools " self.ready = False self.pools_displayed = [] self.page = maya.form_layout(enableBackground=True) with utils.ScrollLayout(v_scrollbar=3, h_scrollbar=0, height=520) as scroll: with utils.RowLayout(row_spacing=20) as sublayout: if not self.pools_displayed: self.empty_pools = maya.text(label="Loading pool data...", font="boldLabelFont", parent=sublayout) self.pools_layout = sublayout with utils.Row(1, 1, 355, "center", (1, "bottom", 0)) as btn: self.refresh_button = utils.ProcButton("Refresh", "Refreshing...", self.refresh) maya.form_layout(self.page, edit=True, attachForm=[(scroll, 'top', 5), (scroll, 'left', 5), (scroll, 'right', 5), (btn, 'bottom', 5), (btn, 'left', 0), (btn, 'right', 0)], attachControl=(scroll, "bottom", 5, btn)) frame.add_tab(self) self.is_logged_out()
def update_pool(self, index): """Update the display for the currently selected pool. This is called when a specific pool is selected or refreshed in the UI. """ try: pool = self.pools[index] self.selected_pool.set_label("loading...") maya.refresh() pool = self._call(self.batch.pool.get, pool.id) _nodes = self._call(self.batch.compute_node.list, pool.id) nodes = [n for n in _nodes] self.selected_pool.set_id(pool.id) self.selected_pool.set_label( pool.display_name if pool.display_name else pool.id) self.selected_pool.set_dedicated_size(pool) self.selected_pool.set_low_pri_size(pool) self.selected_pool.set_type("Auto" if pool.id.startswith( "Maya_Auto_Pool") else "Provisioned") self.selected_pool.set_state(pool.state.value, nodes) self.selected_pool.set_allocation(pool.allocation_state.value) self.selected_pool.set_created(pool.creation_time) self.selected_pool.set_licenses(pool.application_licenses) self.selected_pool.set_vm_sku(pool.vm_size) self.selected_pool.set_image( self.environment.get_image_label( pool.virtual_machine_configuration.image_reference)) maya.refresh() except Exception as exp: self._log.warning(str(exp)) self.ui.refresh()
def disable(self, enabled): """Disable the tab from user interaction. Used during long running processes like upload, and when plug-in is unauthenticated. :param bool enabled: Whether to enable the display. False will disable the display. """ maya.form_layout(self.page, edit=True, enable=enabled)
def __init__(self, id, data_path, dir): """Create a new job watcher. :param str id: The ID of the job to watch. :param str data_path: The path of the AzureBatch config dir. :param str dir: The path of directory where outputs will be downloaded. """ self.job_id = id self.data_path = data_path self.selected_dir = dir self._log = logging.getLogger('AzureBatchMaya') self.job_watcher = os.path.join( os.path.dirname(__file__), "tools", "job_watcher.py") platform = get_os() if platform == OperatingSystem.windows.value: self.proc_cmd = 'system("WMIC PROCESS where (Name=\'mayapy.exe\') get Commandline")' self.start_cmd = 'system("start mayapy {0}")' self.quotes = '\\"' self.splitter = 'mayapy' elif platform == OperatingSystem.darwin.value: self.proc_cmd = 'system("ps -ef")' self.start_cmd = 'system("osascript -e \'tell application \\"Terminal\\" to do script \\"python {0}\\"\'")' self.quotes = '\\\\\\"' self.splitter = '\n' else: maya.warning("Cannot launch job watcher: OS not supported.") return self.start_job_watcher()
def update(self, update): """Update the button label with process status. :param str update: The status to display on the label. """ update = "{0} [Press ESC to cancel]".format(update) maya.button(self._button, edit=True, enable=False, label=update) maya.refresh()
def __init__(self, log): """Create and start new progress bar.""" self.done = False self._log = log self._progress = maya.mel('$tmp = $gMainProgressBar') maya.progress_bar(self._progress, edit=True, beginProgress=True, isInterruptable=True)
def add_row(self, *args): """Add new user environment variable from contents of custom_env text fields. """ env_var = maya.text_field(self.custom_env_var, query=True, text=True) env_val = maya.text_field(self.custom_env_val, query=True, text=True) if env_var and env_val: maya.table(self.env_vars, edit=True, insertRow=1)
def select(self, value): """Select a specific option in the menu. :param int value: The index of the option to select. """ try: maya.menu(self.menu, edit=True, select=int(value)) except ValueError: maya.menu(self.menu, edit=True, value=value)
def update(update): # Update the % complete in the asset label if self.display_text: maya.text(self.display_text, edit=True, label=" Uploading {0}% {1}".format( int(update), name)) maya.refresh()
def include(self): """Include this asset in the list for files to be uploaded for submission.""" if self not in self.parent_list: self.parent_list.append(self) maya.symbol_check_box( self.check_box, edit=True, annotation="Click to remove asset from submission")
def enable_watcher(self, opt): """Enable whether job watcher will be launched after submission. Command for watch_job check box. :param bool opt: True if watcher enabled. """ maya.symbol_button(self.dir_button, edit=True, enable=opt) maya.text_field( self.dir, edit=True, enable=opt, text=self.selected_dir)
def set_job(self, value): """Set the label for the job ID, and format the portal URL reference for the job with this ID. Except that with the current portal we have no way to directly link to a job. :param str value: The job ID. """ maya.text_field(self._job, edit=True, text=value) self.url = "https://portal.azure.com"
def set_submission(self, value): """Set the label for date/time submitted. :param str value: The datetime string to format. """ datetime = value.split('T') datetime[1] = datetime[1].split('.')[0] label = ' '.join(datetime) maya.text(self._submission, edit=True, label=" {0}".format(label))
def __init__(self, base, frame): """Create 'Jobs' tab and add to UI frame. :param base: The base class for handling jobs monitoring functionality. :type base: :class:`.AzureBatchJobHistory` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.label = " Jobs " self.ready = False self.jobs_displayed = [] self.page = maya.form_layout(enableBackground=True) with utils.Row(1, 1, 360, "center") as lbl: self.total = maya.text(label="", font="boldLabelFont") self.paging_display() with utils.ScrollLayout(v_scrollbar=3, h_scrollbar=0, height=478) as scroll: with utils.RowLayout(row_spacing=20) as sublayout: if not self.jobs_displayed: self.empty_jobs = maya.text(label="Loading job data...", align='left', font="boldLabelFont") self.jobs_layout = sublayout with utils.Row(1, 1, 355, "center", (1, "bottom", 0)) as btn: self.refresh_button = utils.ProcButton("Refresh", "Refreshing...", self.refresh) maya.form_layout(self.page, edit=True, attachForm=[(lbl, 'top', 0), (lbl, 'left', 5), (lbl, 'right', 5), (self.first_btn, 'left', 5), (self.last_btn, 'right', 5), (scroll, 'left', 5), (scroll, 'right', 5), (btn, 'bottom', 5), (btn, 'left', 0), (btn, 'right', 0)], attachControl=[(self.first_btn, "top", 5, lbl), (self.prev_btn, "top", 5, lbl), (self.last_btn, "top", 5, lbl), (self.next_btn, "top", 5, lbl), (scroll, "top", 5, self.first_btn), (scroll, "top", 5, self.next_btn), (scroll, "top", 5, self.prev_btn), (scroll, "top", 5, self.last_btn), (scroll, "bottom", 5, btn)], attachPosition=[ (lbl, 'top', 5, 0), (self.first_btn, 'right', 5, 25), (self.prev_btn, 'left', 5, 25), (self.prev_btn, 'right', 5, 50), (self.next_btn, 'right', 5, 75), (self.next_btn, 'left', 5, 50), (self.last_btn, 'left', 5, 75), ]) frame.add_tab(self) self.is_logged_out()
def delete(self): """Remove this asset from the UI display. This is usually done on a refresh of the asset tab in order to wipe the UI for repopulating. """ try: self.parent_list.remove(self) except ValueError: pass maya.delete_ui(self.check_box, control=True)
def clear_ui(self): """Wipe all UI elements in the Assets tab.""" children = maya.col_layout(self.asset_display, query=True, childArray=True) if not children: return for child in children: maya.delete_ui(child, control=True)