text: import logging
  
  from Qt import QtWidgets, QtCore, QtGui
  
  from avalon.vendor import qtawesome, qargparse
  from openpype.style import get_objected_colors
  
  log = logging.getLogger(__name__)
  
  
  class PlaceholderLineEdit(QtWidgets.QLineEdit):
      """Set placeholder color of QLineEdit in Qt 5.12 and higher."""
      def __init__(self, *args, **kwargs):
          super(PlaceholderLineEdit, self).__init__(*args, **kwargs)
          # Change placeholder palette color
          if hasattr(QtGui.QPalette, "PlaceholderText"):
              filter_palette = self.palette()
              color_obj = get_objected_colors()["font"]
              color = color_obj.get_qcolor()
              color.setAlpha(67)
              filter_palette.setColor(
                  QtGui.QPalette.PlaceholderText,
                  color
              )
              self.setPalette(filter_palette)
  
  
  class ImageButton(QtWidgets.QPushButton):
      """PushButton with icon and size of font.
  
      Using font metrics height as icon size reference.
  
      TODO:
      - handle changes of screen (different resolution)
      """
  
      def __init__(self, *args, **kwargs):
          super(ImageButton, self).__init__(*args, **kwargs)
          self.setObjectName("ImageButton")
  
      def _change_size(self):
          font_height = self.fontMetrics().height()
          self.setIconSize(QtCore.QSize(font_height, font_height))
  
      def showEvent(self, event):
          super(ImageButton, self).showEvent(event)
  
          self._change_size()
  
      def sizeHint(self):
          return self.iconSize()
  
  
  class OptionalMenu(QtWidgets.QMenu):
      """A subclass of `QtWidgets.QMenu` to work with `OptionalAction`
  
      This menu has reimplemented `mouseReleaseEvent`, `mouseMoveEvent` and
      `leaveEvent` to provide better action hightlighting and triggering for
      actions that were instances of `QtWidgets.QWidgetAction`.
  
      """
      def mouseReleaseEvent(self, event):
          """Emit option clicked signal if mouse released on it"""
          active = self.actionAt(event.pos())
          if active and active.use_option:
              option = active.widget.option
              if option.is_hovered(event.globalPos()):
                  option.clicked.emit()
          super(OptionalMenu, self).mouseReleaseEvent(event)
  
      def mouseMoveEvent(self, event):
          """Add highlight to active action"""
          active = self.actionAt(event.pos())
          for action in self.actions():
              action.set_highlight(action is active, event.globalPos())
          super(OptionalMenu, self).mouseMoveEvent(event)
  
      def leaveEvent(self, event):
          """Remove highlight from all actions"""
          for action in self.actions():
              action.set_highlight(False)
          super(OptionalMenu, self).leaveEvent(event)
  
  
  class OptionalAction(QtWidgets.QWidgetAction):
      """Menu action with option box
  
      A menu action like Maya's menu item with option box, implemented by
      subclassing `QtWidgets.QWidgetAction`.
  
      """
  
      def __init__(self, label, icon, use_option, parent):
          super(OptionalAction, self).__init__(parent)
          self.label = label
          self.icon = icon
          self.use_option = use_option
          self.option_tip = ""
          self.optioned = False
          self.widget = None
  
      def createWidget(self, parent):
          widget = OptionalActionWidget(self.label, parent)
          self.widget = widget
  
          if self.icon:
              widget.setIcon(self.icon)
  
          if self.use_option:
              widget.option.clicked.connect(self.on_option)
              widget.option.setToolTip(self.option_tip)
          else:
              widget.option.setVisible(False)
  
          return widget
  
      def set_option_tip(self, options):
          sep = "\n\n"
          mak = (lambda opt: opt["name"] + " :\n    " + opt["help"])
          self.option_tip = sep.join(mak(opt) for opt in options)
  
      def on_option(self):
          self.optioned = True
  
      def set_highlight(self, state, global_pos=None):
          option_state = False
          if self.use_option:
              option_state = self.widget.option.is_hovered(global_pos)
          self.widget.set_hover_properties(state, option_state)
  
  
  class OptionalActionWidget(QtWidgets.QWidget):
      """Main widget class for `OptionalAction`"""
  
      def __init__(self, label, parent=None):
          super(OptionalActionWidget, self).__init__(parent)
  
          body_widget = QtWidgets.QWidget(self)
          body_widget.setObjectName("OptionalActionBody")
  
          icon = QtWidgets.QLabel(body_widget)
          label = QtWidgets.QLabel(label, body_widget)
          # (NOTE) For removing ugly QLable shadow FX when highlighted in Nuke.
          #   See https://stackoverflow.com/q/52838690/4145300
          label.setStyle(QtWidgets.QStyleFactory.create("Plastique"))
          option = OptionBox(body_widget)
          option.setObjectName("OptionalActionOption")
  
          icon.setFixedSize(24, 16)
          option.setFixedSize(30, 30)
  
          body_layout = QtWidgets.QHBoxLayout(body_widget)
          body_layout.setContentsMargins(4, 0, 4, 0)
          body_layout.setSpacing(2)
          body_layout.addWidget(icon)
          body_layout.addWidget(label)
  
          layout = QtWidgets.QHBoxLayout(self)
          layout.setContentsMargins(2, 1, 2, 1)
          layout.setSpacing(0)
          layout.addWidget(body_widget)
          layout.addWidget(option)
  
          body_widget.setMouseTracking(True)
          label.setMouseTracking(True)
          option.setMouseTracking(True)
          self.setMouseTracking(True)
          self.setFixedHeight(32)
  
          self.icon = icon
          self.label = label
          self.option = option
          self.body = body_widget
  
      def set_hover_properties(self, hovered, option_hovered):
          body_state = ""
          option_state = ""
          if hovered:
              body_state = "hover"
  
          if option_hovered:
              option_state = "hover"
  
          if self.body.property("state") != body_state:
              self.body.setProperty("state", body_state)
              self.body.style().polish(self.body)
  
          if self.option.property("state") != option_state:
              self.option.setProperty("state", option_state)
              self.option.style().polish(self.option)
  
      def setIcon(self, icon):
          pixmap = icon.pixmap(16, 16)
          self.icon.setPixmap(pixmap)
  
  
  class OptionBox(QtWidgets.QLabel):
      """Option box widget class for `OptionalActionWidget`"""
  
      clicked = QtCore.Signal()
  
      def __init__(self, parent):
          super(OptionBox, self).__init__(parent)
  
          self.setAlignment(QtCore.Qt.AlignCenter)
  
          icon = qtawesome.icon("fa.sticky-note-o", color="#c6c6c6")
          pixmap = icon.pixmap(18, 18)
          self.setPixmap(pixmap)
  
      def is_hovered(self, global_pos):
          if global_pos is None:
              return False
          pos = self.mapFromGlobal(global_pos)
          return self.rect().contains(pos)
  
  
  class OptionDialog(QtWidgets.QDialog):
      """Option dialog shown by option box"""
  
      def __init__(self, parent=None):
          super(OptionDialog, self).__init__(parent)
          self.setModal(True)
          self._options = dict()
  
      def create(self, options):
          parser = qargparse.QArgumentParser(arguments=options)
  
          decision_widget = QtWidgets.QWidget(self)
          accept_btn = QtWidgets.QPushButton("Accept", decision_widget)
          cancel_btn = QtWidgets.QPushButton("Cancel", decision_widget)
  
          decision_layout = QtWidgets.QHBoxLayout(decision_widget)
          decision_layout.addWidget(accept_btn)
          decision_layout.addWidget(cancel_btn)
  
          layout = QtWidgets.QVBoxLayout(self)
          layout.addWidget(parser)
          layout.addWidget(decision_widget)
  
          accept_btn.clicked.connect(self.accept)
          cancel_btn.clicked.connect(self.reject)
          parser.changed.connect(self.on_changed)
  
      def on_changed(self, argument):
          self._options[argument["name"]] = argument.read()
  
      def parse(self):
          return self._options.copy()
  