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 init_post_auth(self): maya.delete_ui(self.auth_temp_ui_elements) if self.subscription_ui_elements is not None and not len( self.subscription_ui_elements) == 0: maya.delete_ui(self.subscription_ui_elements) if self.subscription_row is not None: maya.delete_ui(self.subscription_row) 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.account_ui_elements = [] self.subscription_ui_elements = [] box_label = "Batch Account Settings" with utils.FrameLayout( label=box_label, collapsable=True, width=345, collapse=False, parent=self.frames_layout) as batch_account_framelayout: self.batch_account_framelayout = batch_account_framelayout with utils.Row(1, 1, (300), ("left"), [1, "top", 15]): self.account_status_field = maya.text(label="", align="center") self.account_status = "Please select Subscription" with utils.Row(2, 2, (100, 200), ("left", "left")) as subscriptionRow: self.subscription_row = subscriptionRow maya.text(label="Subscription: ", align="right") with utils.Dropdown(self.select_subscription_in_dropdown ) as subscription_dropdown: self._subscription_dropdown = subscription_dropdown self._subscription_dropdown.add_item("") self.subscription_ui_elements.append( self._subscription_dropdown) subscriptions = self.base.available_subscriptions() self.subscriptions_by_displayname = dict([ (sub.display_name, sub) for sub in subscriptions ]) for sub in subscriptions: self._subscription_dropdown.add_item(sub.display_name) self.draw_threads_and_logging_rows(self.frames_layout, self.account_ui_elements) self.auth_status = "Authenticated" self.disable(True) maya.refresh()
def draw_threads_and_logging_rows(self, parent, element_list): box_label = "Plugin Settings" if self.plugin_settings_framelayout is not None: maya.delete_ui(self.plugin_settings_framelayout) with utils.FrameLayout(label=box_label, collapsable=True, width=345, collapse=True, parent=parent) as plugin_settings_framelayout: self.plugin_settings_framelayout = plugin_settings_framelayout element_list.append(plugin_settings_framelayout) with utils.Row(2, 2, (100, 200), ("left", "left"), parent=plugin_settings_framelayout) as threadsRow: element_list.append(threadsRow) maya.text(label="Threads: ", align="right") self._threads = maya.int_field( changeCommand=self.set_threads, annotation= "The maximum number of parallel threads to use for uploading of assets", height=25, minValue=1, maxValue=40, enable=True, value=self.base.threads) with utils.Row(2, 2, (100, 200), ("left", "center"), parent=plugin_settings_framelayout) as loggingRow: element_list.append(loggingRow) maya.text(label="Logging: ", align="right") with utils.Dropdown(self.set_logging) as log_settings: self._logging = log_settings self._logging.add_item("Debug") self._logging.add_item("Info") self._logging.add_item("Warning") self._logging.add_item("Error")
def init_post_config_file_read(self): maya.delete_ui(self.heading) self.heading = None box_label = "AzureActiveDirectory Authentication" aad_domain_hover_message = "Sign in to the Azure portal and hover over your account name on the upper right-hand side, your AAD Domain will be the bottom value shown, e.g. \"contoso.onmicrosoft.com\"" with utils.FrameLayout(label=box_label, collapsable=True, width=345, collapse=False, parent=self.frames_layout) as aad_framelayout: self.aad_framelayout = aad_framelayout with utils.Row(1, 1, (300), ("left"), [1, "top", 15], parent=self.aad_framelayout): self.auth_status_field = maya.text(label="", align="center") with utils.Row(2, 2, (100, 200), ("right", "center"), parent=self.aad_framelayout) as aad_environment_row: self.aad_environment_row = aad_environment_row maya.text(label="Environment: ", align="right", parent=self.aad_environment_row, annotation=aad_domain_hover_message) with utils.Dropdown( self.aad_environment_changed, parent=aad_environment_row) as aad_environment_dropdown: self.aad_environment_dropdown = aad_environment_dropdown for id in self.base.aad_environment_provider.getAADEnvironments( ): self.aad_environment_dropdown.add_item(id) if self.base.aad_environment_id: self.aad_environment_dropdown.select( self.base.aad_environment_id) else: self.aad_environment_dropdown.select("AzureCloud") with utils.Row(2, 2, (100, 200), ("right", "center"), parent=self.aad_framelayout) as aad_tenant_row: self.aad_tenant_row = aad_tenant_row maya.text(label="AAD Domain: ", align="right", parent=self.aad_tenant_row, annotation=aad_domain_hover_message) #Registering changeCommand captures changes to the field when not confirmed with enter. #Registering enterCommand with alwaysInvokeEnterCommandOnReturn allows the same value to be reentered so as to generate a new login prompt. self.aad_tenant_field = maya.text_field( height=25, enable=True, changeCommand=self.aad_tenant_name_changed, enterCommand=self.aad_tenant_name_changed, alwaysInvokeEnterCommandOnReturn=True, annotation=aad_domain_hover_message, parent=self.aad_tenant_row) maya.form_layout(self.page, edit=True, enable=False, attachForm=[(self.scroll_layout, 'top', 5), (self.scroll_layout, 'left', 5), (self.scroll_layout, 'right', 5)]) cached_tenant_name = self.base.aad_tenant_name if cached_tenant_name and cached_tenant_name != None and cached_tenant_name != 'None': maya.text_field(self.aad_tenant_field, edit=True, text=cached_tenant_name)
def init_from_config(self): maya.delete_ui(self.auth_temp_ui_elements) self.subscription_ui_elements = [] box_label = "Batch Account Settings" with utils.FrameLayout( label=box_label, collapsable=True, width=345, collapse=False, parent=self.frames_layout) as batch_account_framelayout: self.batch_account_framelayout = batch_account_framelayout with utils.Row(1, 1, (300), ("left"), [1, "top", 15], parent=self.batch_account_framelayout): self.account_status_field = maya.text(label="", align="center") self.account_status = maya.text(label="", align="center") with utils.Row(3, 3, (100, 170, 30), ("left", "left", "left")) as subscriptionRow: maya.text(label="Subscription: ", align="left") with utils.Dropdown(self.select_subscription_in_dropdown, enable=False) as subscription_dropdown: self._subscription_dropdown = subscription_dropdown self.subscription_ui_elements.append( self._subscription_dropdown) self._subscription_dropdown.add_item( self.base.subscription_name) self._change_subscription_button = maya.button( label="Change", command=self.change_subscription_button_pressed, width=30) self.subscription_row = subscriptionRow with utils.Row(3, 3, (100, 170, 30), ("left", "left", "left")) as batch_account_row: maya.text(label="Batch Account: ", align="left") maya.refresh() with utils.Dropdown(self.select_account_in_dropdown, enable=False) as account_dropdown: self._account_dropdown = account_dropdown self._account_dropdown.add_item(self.base.batch_account) self._change_batch_account_button = maya.button( label="Change", command=self.change_batch_account_button_pressed, width=30) self.batch_account_row = batch_account_row #TODO test the case that autostorage is removed from a stored account in config self.account_ui_elements = [] with utils.Row(2, 2, (100, 200), ("left", "left"), parent=self.batch_account_framelayout ) as storageAccountRow: self.account_ui_elements.append(storageAccountRow) maya.text(label="Storage Account:", align="left") self.storage_account_field = maya.text_field( height=25, enable=True, editable=False, text=self.base.storage_account) self.draw_threads_and_logging_rows(self.frames_layout, self.account_ui_elements) self.account_status = "Batch Account Configured" self.auth_status = "Authenticated" self.disable(True) maya.form_layout(self.page, edit=True, enable=True) maya.refresh()
def __init__(self, base, frame, images, skus, licenses): """Create 'Env' tab and add to UI frame. :param base: The base class for handling Maya and plugin-related functionality. :type base: :class:`.AzureBatchEnvironment` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.label = " Env " self.ready = False self.page = maya.form_layout(enableBackground=True) self.license_settings = {} with utils.ScrollLayout(v_scrollbar=3, h_scrollbar=0, height=520) as scroll: with utils.RowLayout(row_spacing=20) as sublayout: with utils.FrameLayout(label="Render Node Configuration", collapsable=True, width=325): with utils.ColumnLayout(2, col_width=((1, 160), (2, 160)), row_spacing=(1, 5), row_offset=((1, "top", 15), (5, "bottom", 15))): maya.text(label="Use Image: ", align='right') with utils.Dropdown(self.set_image) as image_settings: self._image = image_settings for image in images: self._image.add_item(image) maya.text(label="Use VM Type: ", align='right') with utils.Dropdown(self.set_sku) as sku_settings: self._sku = sku_settings for sku in skus: self._sku.add_item(sku) maya.text(label="Use licenses: ", align='right') for label, checked in licenses.items(): self.license_settings[label] = maya.check_box( label=label, value=checked, changeCommand=self.use_license_server) maya.text(label="", align='right') with utils.FrameLayout(label="Environment Variables", collapsable=True, width=325, collapse=True): with utils.Row(1, 1, 325): self.env_vars = maya.table( rows=0, columns=2, columnWidth=[(1, 155), (2, 155)], label=[(1, "Setting"), (2, "Value")], rowHeight=15, editable=False, selectionBehavior=1, getCellCmd=self.populate_row) with utils.ColumnLayout(2, col_width=((1, 160), (2, 160))): self.custom_env_var = maya.text_field( placeholderText='Env Variable') self.custom_env_val = maya.text_field( placeholderText='Value') maya.button(label="Add", command=self.add_row) maya.button(label="Delete", command=self.delete_row) 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', 5), (btn, 'right', 5)], attachControl=(scroll, "bottom", 5, btn)) frame.add_tab(self) self.is_logged_out()
def __init__(self, base, frame, images, skus, licenses): """Create 'Env' tab and add to UI frame. :param base: The base class for handling Maya and plugin-related functionality. :type base: :class:`.AzureBatchEnvironment` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.label = " Env " self.ready = False self.page = maya.form_layout(enableBackground=True) self.licenses = licenses self.batch_images = images self.poolImageFilter = PoolImageFilter(PoolImageProvider()) self.image_config = None self.license_settings = {} self.node_sku_id = "" self.containerImageUI = None self.select_rendernode_type = PoolImageMode.MARKETPLACE_IMAGE.value with utils.ScrollLayout(v_scrollbar=3, h_scrollbar=0, height=520) as scroll: with utils.RowLayout(row_spacing=20) as sublayout: with utils.FrameLayout(label="Render Node Configuration", collapsable=True, width=325, collapse=False) as rendernode_config: self.rendernode_config = rendernode_config with utils.ColumnLayout(2, col_width=((1, 100), (2, 200)), row_spacing=(1, 5), row_offset=((1, "top", 15), (2, "bottom", 20))): maya.text(label="Use VM SKU: ", align='left') with utils.Dropdown(self.set_sku) as sku_settings: self._sku = sku_settings for sku in skus: self._sku.add_item(sku) with utils.Row(1, 1, 325): maya.radio_group( labelArray2=("Batch Provided Image", "Container Image"), numberOfRadioButtons=2, select=self.select_rendernode_type, vertical=True, onCommand1=self.set_marketplace_image_mode, onCommand2=self.set_container_image_mode) maya.parent() self.set_marketplace_image_mode() with utils.FrameLayout(label="Application Licenses", collapsable=True, width=325, collapse=True): with utils.ColumnLayout(2, col_width=((1, 100), (2, 200)), row_spacing=(1, 5), row_offset=((1, "top", 15))): maya.text(label="Use licenses: ", align='right') for label, checked in licenses.items(): self.license_settings[label] = maya.check_box( label=label, value=checked, changeCommand=self.use_license) maya.text(label="", align='right') with utils.FrameLayout(label="Environment Variables", collapsable=True, width=325, collapse=True): with utils.Row(1, 1, 325): self.env_vars = maya.table( height=95, rows=0, columns=2, columnWidth=[(1, 190), (2, 190)], label=[(1, "Setting"), (2, "Value")], rowHeight=15, editable=False, selectionBehavior=1, getCellCmd=self.populate_row) with utils.ColumnLayout(2, col_width=((1, 200), (2, 200))): self.custom_env_var = maya.text_field( placeholderText='Env Variable') self.custom_env_val = maya.text_field( placeholderText='Value') maya.button(label="Add", command=self.add_row) maya.button(label="Delete", command=self.delete_row) with utils.Row(1, 1, 355, "center", (1, "bottom", 0)) as btn: self.refresh_button = utils.ProcButton("Refresh", "Refreshing...", self.refresh_btn_clicked) maya.form_layout(self.page, edit=True, attachForm=[(scroll, 'top', 5), (scroll, 'left', 5), (scroll, 'right', 5), (btn, 'bottom', 5), (btn, 'left', 5), (btn, 'right', 5)], attachControl=(scroll, "bottom", 5, btn)) frame.add_tab(self) self.is_logged_out()
def __init__(self, base, frame): """Create 'Config' tab and add to UI frame. :param base: The base class for handling configuration and auth-related functionality. :type base: :class:`.AzureBatchConfig` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.label = "Config" self.ready = False self.page = maya.form_layout() with utils.ScrollLayout(height=520, parent=self.page) as scroll: self.heading = maya.text(label="Authenticating plug-in...", align="center", font="boldLabelFont") with utils.Row(2, 2, (70,260), ("left","left"), [(1, "top", 15),(2,"top",15)]): maya.text(label="Status: ", align="left") self.auth_status = maya.text(label="", align="left") with utils.Row(2, 2, (70,260), ("left","left")): maya.text(label="Service: ", align="left") self._endpoint = maya.text_field(height=25, enable=True) #TODO: Allow set to 0 to disable threads with utils.Row(2, 2, (70,260), ("left","left")): maya.text(label="Threads: ", align="left") self._threads = maya.int_field( changeCommand=self.set_threads, height=25, minValue=1, maxValue=40, enable=True, value=20) with utils.Row(2, 2, (70,260), ("left","center"), [(1, "bottom", 20),(2,"bottom",15)]): maya.text(label="Logging: ", align="left") with utils.Dropdown(self.set_logging) as log_settings: self._logging = log_settings self._logging.add_item("Debug") self._logging.add_item("Info") self._logging.add_item("Warning") self._logging.add_item("Error") box_label = "Azure Authentication Settings" with utils.FrameLayout(label=box_label, collapsable=True): with utils.Row(2, 2, (140, 180), ("right","center"), [(1, "top", 20),(2, "top", 15)]): maya.text(label="Batch Account: ", align="right") self._account = maya.text_field(height=25, enable=True) with utils.Row(2, 2, (140, 180), ("right","center"), [(1, "bottom", 20),(2,"bottom",15)]): maya.text(label="Batch Key: ", align="right") self._key = maya.text_field(height=25, enable=True) with utils.Row(2, 2, (140, 180), ("right","center"), [(1, "bottom", 20),(2,"bottom",15)]): maya.text(label="Storage Account: ", align="right") self._storage = maya.text_field(height=25, enable=True) with utils.Row(2, 2, (140, 180), ("right","center"), [(1, "bottom", 20),(2,"bottom",15)]): maya.text(label="Storage Key: ", align="right") self._storage_key = maya.text_field(height=25, enable=True) with utils.Row(1, 1, 355, "center", (1, "bottom",0)) as btn: self._authenticate = maya.button(label="Authenticate", command=self.authenticate, enable=True) 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) maya.form_layout(self.page, edit=True, enable=False) maya.refresh()
def __init__(self, poolImageFilter, parent, image_config, renderer): self.poolImageFilter = poolImageFilter self.renderer = renderer self.selected_os = None self.selected_maya = None self.selected_vray = None self.selected_arnold = None with utils.FrameLayout(label="Container Image Settings", collapsable=True, width=325, collapse=False, parent=parent) as framelayout: image_config.append(framelayout) with utils.ColumnLayout(2, col_width=((1, 100), (2, 200)), row_spacing=(1, 5), row_offset=((1, "top", 15), (5, "bottom", 15)), parent=framelayout) as imageLayout: image_config.append(imageLayout) image_config.append( maya.text(label="OS : ", align='left', parent=imageLayout)) with utils.Dropdown(self.os_dropdown_set, parent=imageLayout) as os_dropdown: image_config.append(os_dropdown) self.os_dropdown = os_dropdown for os_version in self.poolImageFilter.getOSDisplayList(): self.os_dropdown.add_item(os_version) self.selected_os = self.os_dropdown.value() image_config.append( maya.text(label="Maya Version : ", align='left', parent=imageLayout)) with utils.Dropdown(self.maya_dropdown_set, parent=imageLayout) as maya_dropdown: image_config.append(maya_dropdown) self.maya_dropdown = maya_dropdown for maya_version in self.poolImageFilter.getMayaDisplayList( self.selected_os): self.maya_dropdown.add_item(maya_version) self.selected_maya = self.maya_dropdown.value() if self.renderer == "vray": image_config.append( maya.text(label="VRay Version : ", align='left', parent=imageLayout)) with utils.Dropdown(self.vray_dropdown_set, parent=imageLayout) as vray_dropdown: image_config.append(vray_dropdown) self.vray_dropdown = vray_dropdown for vray_version in self.poolImageFilter.getVrayDisplayList( self.selected_os, self.selected_maya): self.vray_dropdown.add_item(vray_version) self.selected_vray = self.vray_dropdown.value() if self.renderer == "arnold": image_config.append( maya.text(label="Arnold Version : ", align='left', parent=imageLayout)) with utils.Dropdown(self.arnold_dropdown_set, parent=imageLayout) as arnold_dropdown: image_config.append(arnold_dropdown) self.arnold_dropdown = arnold_dropdown for arnold_version in self.poolImageFilter.getArnoldDisplayList( self.selected_os, self.selected_maya): self.arnold_dropdown.add_item(arnold_version) self.selected_arnold = self.arnold_dropdown.value()
def __init__(self, base, frame): """Create 'Submit' tab and add to UI frame. :param base: The base class for handling jobs submission functionality. :type base: :class:`.AzureBatchSubmission` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.label = "Submit" self.page = maya.form_layout(enableBackground=True) self.select_pool_type = self.AUTO_POOL self.select_dedicated_instances = 1 self.select_low_pri_instances = 0 with utils.ScrollLayout(height=475, parent=self.page) as scroll: box_label = "Pool Settings" with utils.FrameLayout(label=box_label, collapsable=True) as pool_settings: self.pool_settings = pool_settings maya.col_layout(numberOfColumns=2, columnWidth=((1, 100), (2, 200)), rowSpacing=(1, 10), rowOffset=((1, "top", 20), )) maya.text(label="Pools: ", align="right") maya.radio_group( labelArray3=("Auto provision a pool for this job", "Reuse an existing persistent pool", "Create a new persistent pool"), numberOfRadioButtons=3, select=self.select_pool_type, vertical=True, onCommand1=self.set_pool_auto, onCommand2=self.set_pool_reuse, onCommand3=self.set_pool_new) maya.parent() self.pool_config = [] self.pool_config.append( maya.col_layout(numberOfColumns=4, columnWidth=((1, 100), (2, 50), (3, 100), (4, 50)), rowSpacing=(1, 10), rowOffset=((1, "bottom", 20), ), parent=self.pool_settings)) self.pool_config.append( maya.text(label="Dedicated VMs: ", align="right", parent=self.pool_config[0])) self.pool_config.append( maya.int_field( value=self.select_dedicated_instances, minValue=1, maxValue=self.base.max_pool_size, changeCommand=self.set_dedicated_instances, annotation="Number of dedicated VMs in pool", parent=self.pool_config[0])) self.pool_config.append( maya.text(label="Low-pri VMs: ", align="right", parent=self.pool_config[0])) self.pool_config.append( maya.int_field( value=self.select_low_pri_instances, minValue=0, maxValue=self.base.max_pool_size, changeCommand=self.set_low_pri_instances, annotation="Number of low-priority VMs in pool", parent=self.pool_config[0])) maya.parent() box_label = "Render Settings" with utils.FrameLayout(label=box_label, collapsable=True) as box: self.render_module = box with utils.Row(3, 2, (120, 200, 35)) as watch: self.job_watcher_ui() with utils.Row(1, 1, 355, "center") as s_btn: self.submit_button = utils.ProcButton("Submit Job", "Submitting...", self.submit) with utils.Row(1, 1, 355, "center", (1, "bottom", 0)) as r_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), (watch, 'left', 0), (watch, 'right', 0), (s_btn, 'left', 0), (s_btn, 'right', 0), (r_btn, 'bottom', 5), (r_btn, 'left', 0), (r_btn, 'right', 0)], attachControl=[(scroll, "bottom", 5, watch), (watch, "bottom", 5, s_btn), (s_btn, "bottom", 5, r_btn)]) frame.add_tab(self)
def __init__(self, base, frame): """Create 'Submit' tab and add to UI frame. :param base: The base class for handling jobs submission functionality. :type base: :class:`.AzureBatchSubmission` :param frame: The shared plug-in UI frame. :type frame: :class:`.AzureBatchUI` """ self.base = base self.frame = frame self.label = "Submit" self.page = maya.form_layout(enableBackground=True) self.select_dedicated_instances = 1 self.select_low_pri_instances = 0 self.selected_container_image = None self.container_image_text_row = None self.container_image_dropdown_row = None self.persistent_pool_dropdown_row = None self.render_node_config_row = None self.reused_pool_id = None self.container_config = [] self.select_pool_type = self.AUTO_POOL with utils.ScrollLayout(height=475, parent=self.page) as scroll: box_label = "Pool Settings" with utils.FrameLayout(label=box_label, collapsable=True) as pool_settings: self.pool_settings = pool_settings with utils.ColumnLayout( 2, col_width=((1,100),(2,200)), row_spacing=(1,10), row_offset=((1, "top", 20),(5, "bottom", 15))): maya.text(label="Pools: ", align="right") maya.radio_group( labelArray3=("Auto provision a pool for this job", "Reuse an existing persistent pool", "Create a new persistent pool"), numberOfRadioButtons=3, select=self.select_pool_type, vertical=True, onCommand1=self.set_pool_auto, onCommand2=self.set_pool_reuse, onCommand3=self.set_pool_new) self.pool_config = [] box_label = "Render Settings" with utils.FrameLayout(label=box_label, collapsable=True) as box: self.render_module = box with utils.Row(3, 2, (120,200,35)) as watch: self.job_watcher_ui() with utils.Row(1, 1, 355, "center") as s_btn: self.submit_button = utils.ProcButton( "Submit Job", "Submitting...", self.submit) with utils.Row(1, 1, 355, "center", (1,"bottom",0)) as r_btn: self.refresh_button = utils.ProcButton( "Refresh", "Refreshing...", self.refresh_btn_clicked) maya.form_layout( self.page, edit=True, attachForm=[(scroll, 'top', 5), (scroll, 'left', 5), (scroll, 'right', 5), (watch, 'left', 0), (watch, 'right', 0), (s_btn, 'left', 0), (s_btn, 'right', 0), (r_btn, 'bottom', 5), (r_btn, 'left', 0), (r_btn, 'right', 0)], attachControl=[(scroll, "bottom", 5, watch), (watch, "bottom" ,5, s_btn), (s_btn, "bottom", 5, r_btn)]) frame.add_tab(self)