text: import sys
  import time
  import logging
  
  from Qt import QtWidgets, QtCore
  
  from openpype.client import get_last_version_by_subset_id
  from openpype import style
  from openpype.pipeline import legacy_io
  from openpype.tools.utils.lib import qt_app_context
  from openpype.hosts.maya.api.lib import assign_look_by_version
  
  from maya import cmds
  # old api for MFileIO
  import maya.OpenMaya
  import maya.api.OpenMaya as om
  
  from .widgets import (
      AssetOutliner,
      LookOutliner
  )
  from .commands import (
      get_workfile,
      remove_unused_looks
  )
  from .vray_proxies import vrayproxy_assign_look
  
  module = sys.modules[__name__]
  module.window = None
  
  
  class MayaLookAssignerWindow(QtWidgets.QWidget):
  
      def __init__(self, parent=None):
          super(MayaLookAssignerWindow, self).__init__(parent=parent)
  
          self.log = logging.getLogger(__name__)
  
          # Store callback references
          self._callbacks = []
          self._connections_set_up = False
  
          filename = get_workfile()
  
          self.setObjectName("lookManager")
          self.setWindowTitle("Look Manager 1.3.0 - [{}]".format(filename))
          self.setWindowFlags(QtCore.Qt.Window)
          self.setParent(parent)
  
          self.resize(750, 500)
  
          self.setup_ui()
  
          # Force refresh check on initialization
          self._on_renderlayer_switch()
  
      def setup_ui(self):
          """Build the UI"""
  
          main_splitter = QtWidgets.QSplitter(self)
  
          # Assets (left)
          asset_outliner = AssetOutliner(main_splitter)
  
          # Looks (right)
          looks_widget = QtWidgets.QWidget(main_splitter)
  
          look_outliner = LookOutliner(looks_widget)  # Database look overview
  
          assign_selected = QtWidgets.QCheckBox(
              "Assign to selected only", looks_widget
          )
          assign_selected.setToolTip("Whether to assign only to selected nodes "
                                     "or to the full asset")
          remove_unused_btn = QtWidgets.QPushButton(
              "Remove Unused Looks", looks_widget
          )
  
          looks_layout = QtWidgets.QVBoxLayout(looks_widget)
          looks_layout.addWidget(look_outliner)
          looks_layout.addWidget(assign_selected)
          looks_layout.addWidget(remove_unused_btn)
  
          main_splitter.addWidget(asset_outliner)
          main_splitter.addWidget(looks_widget)
          main_splitter.setSizes([350, 200])
  
          # Footer
          status = QtWidgets.QStatusBar(self)
          status.setSizeGripEnabled(False)
          status.setFixedHeight(25)
          warn_layer = QtWidgets.QLabel(
              "Current Layer is not defaultRenderLayer", self
          )
          warn_layer.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
          warn_layer.setStyleSheet("color: #DD5555; font-weight: bold;")
          warn_layer.setFixedHeight(25)
  
          footer = QtWidgets.QHBoxLayout()
          footer.setContentsMargins(0, 0, 0, 0)
          footer.addWidget(status)
          footer.addWidget(warn_layer)
  
          # Build up widgets
          main_layout = QtWidgets.QVBoxLayout(self)
          main_layout.setSpacing(0)
          main_layout.addWidget(main_splitter)
          main_layout.addLayout(footer)
  
          # Set column width
          asset_outliner.view.setColumnWidth(0, 200)
          look_outliner.view.setColumnWidth(0, 150)
  
          asset_outliner.selection_changed.connect(
              self.on_asset_selection_changed)
  
          asset_outliner.refreshed.connect(
              lambda: self.echo("Loaded assets..")
          )
  
          look_outliner.menu_apply_action.connect(self.on_process_selected)
          remove_unused_btn.clicked.connect(remove_unused_looks)
  
          # Open widgets
          self.asset_outliner = asset_outliner
          self.look_outliner = look_outliner
          self.status = status
          self.warn_layer = warn_layer
  
          # Buttons
          self.remove_unused = remove_unused_btn
          self.assign_selected = assign_selected
  
          self._first_show = True
  
      def setup_connections(self):
          """Connect interactive widgets with actions"""
          if self._connections_set_up:
              return
  
          # Maya renderlayer switch callback
          callback = om.MEventMessage.addEventCallback(
              "renderLayerManagerChange",
              self._on_renderlayer_switch
          )
          self._callbacks.append(callback)
          self._connections_set_up = True
  
      def remove_connection(self):
          # Delete callbacks
          for callback in self._callbacks:
              om.MMessage.removeCallback(callback)
  
          self._callbacks = []
          self._connections_set_up = False
  
      def showEvent(self, event):
          self.setup_connections()
          super(MayaLookAssignerWindow, self).showEvent(event)
          if self._first_show:
              self._first_show = False
              self.setStyleSheet(style.load_stylesheet())
  
      def closeEvent(self, event):
          self.remove_connection()
          super(MayaLookAssignerWindow, self).closeEvent(event)
  
      def _on_renderlayer_switch(self, *args):
          """Callback that updates on Maya renderlayer switch"""
  
          if maya.OpenMaya.MFileIO.isNewingFile():
              # Don't perform a check during file open or file new as
              # the renderlayers will not be in a valid state yet.
              return
  
          layer = cmds.editRenderLayerGlobals(query=True,
                                              currentRenderLayer=True)
          if layer != "defaultRenderLayer":
              self.warn_layer.show()
          else:
              self.warn_layer.hide()
  
      def echo(self, message):
          self.status.showMessage(message, 1500)
  
      def refresh(self):
          """Refresh the content"""
  
          # Get all containers and information
          self.asset_outliner.clear()
          found_items = self.asset_outliner.get_all_assets()
          if not found_items:
              self.look_outliner.clear()
  
      def on_asset_selection_changed(self):
          """Get selected items from asset loader and fill look outliner"""
  
          items = self.asset_outliner.get_selected_items()
          self.look_outliner.clear()
          self.look_outliner.add_items(items)
  
      def on_process_selected(self):
          """Process all selected looks for the selected assets"""
  
          assets = self.asset_outliner.get_selected_items()
          assert assets, "No asset selected"
  
          # Collect the looks we want to apply (by name)
          look_items = self.look_outliner.get_selected_items()
          looks = {look["subset"] for look in look_items}
  
          selection = self.assign_selected.isChecked()
          asset_nodes = self.asset_outliner.get_nodes(selection=selection)
  
          project_name = legacy_io.active_project()
          start = time.time()
          for i, (asset, item) in enumerate(asset_nodes.items()):
  
              # Label prefix
              prefix = "({}/{})".format(i + 1, len(asset_nodes))
  
              # Assign the first matching look relevant for this asset
              # (since assigning multiple to the same nodes makes no sense)
              assign_look = next((subset for subset in item["looks"]
                                  if subset["name"] in looks), None)
              if not assign_look:
                  self.echo(
                      "{} No matching selected look for {}".format(prefix, asset)
                  )
                  continue
  
              # Get the latest version of this asset's look subset
              version = get_last_version_by_subset_id(
                  project_name, assign_look["_id"], fields=["_id"]
              )
  
              subset_name = assign_look["name"]
              self.echo("{} Assigning {} to {}\t".format(
                  prefix, subset_name, asset
              ))
              nodes = item["nodes"]
  
              if cmds.pluginInfo('vrayformaya', query=True, loaded=True):
                  self.echo("Getting vray proxy nodes ...")
                  vray_proxies = set(cmds.ls(type="VRayProxy", long=True))
  
                  if vray_proxies:
                      for vp in vray_proxies:
                          if vp in nodes:
                              vrayproxy_assign_look(vp, subset_name)
  
                      nodes = list(set(item["nodes"]).difference(vray_proxies))
  
                  # Assign look
              if nodes:
                  assign_look_by_version(nodes, version_id=version["_id"])
  
          end = time.time()
  
          self.echo("Finished assigning.. ({0:.3f}s)".format(end - start))
  
  
  def show():
      """Display Loader GUI
  
      Arguments:
          debug (bool, optional): Run loader in debug-mode,
              defaults to False
  
      """
  
      try:
          module.window.close()
          del module.window
      except (RuntimeError, AttributeError):
          pass
  
      # Get Maya main window
      top_level_widgets = QtWidgets.QApplication.topLevelWidgets()
      mainwindow = next(widget for widget in top_level_widgets
                        if widget.objectName() == "MayaWindow")
  
      with qt_app_context():
          window = MayaLookAssignerWindow(parent=mainwindow)
          window.show()
  
          module.window = window
  