moved qargparse into openpype vendor

This commit is contained in:
Jakub Trllo 2022-03-07 17:22:51 +01:00
parent c57fc03916
commit e48af45857
12 changed files with 833 additions and 15 deletions

View file

@ -2,9 +2,9 @@ import os
import re
import shutil
import sys
from avalon.vendor import qargparse
from xml.etree import ElementTree as ET
import six
import qargparse
from Qt import QtWidgets, QtCore
import openpype.api as openpype
from openpype import style

View file

@ -2,7 +2,7 @@ import re
import os
import hiero
from Qt import QtWidgets, QtCore
from avalon.vendor import qargparse
import qargparse
import avalon.api as avalon
import openpype.api as openpype
from . import lib

View file

@ -2,8 +2,9 @@ import os
from maya import cmds
import qargparse
from avalon import api
from avalon.vendor import qargparse
from openpype.api import PypeCreatorMixin
from .pipeline import containerise

View file

@ -1,5 +1,5 @@
import nuke
from avalon.vendor import qargparse
import qargparse
from avalon import api, io
from openpype.hosts.nuke.api.lib import (

View file

@ -1,7 +1,6 @@
import re
import nuke
from avalon.vendor import qargparse
import qargparse
from avalon import api, io
from openpype.hosts.nuke.api.lib import (

View file

@ -1,7 +1,7 @@
import os
import qargparse
from avalon.pipeline import get_representation_path_from_context
from avalon.vendor import qargparse
from openpype.hosts.photoshop import api as photoshop
from openpype.hosts.photoshop.api import get_unique_layer_name
@ -92,4 +92,3 @@ class ImageFromSequenceLoader(photoshop.PhotoshopLoader):
def remove(self, container):
"""No update possible, not containerized."""
pass

View file

@ -1,12 +1,14 @@
import re
import uuid
import qargparse
from Qt import QtWidgets, QtCore
from avalon import api
import openpype.api as pype
from openpype.hosts import resolve
from avalon.vendor import qargparse
from . import lib
from Qt import QtWidgets, QtCore
class CreatorWidget(QtWidgets.QDialog):

View file

@ -1,4 +1,4 @@
from avalon.vendor import qargparse
import qargparse
from openpype.hosts.tvpaint.api import lib, plugin

View file

@ -1,6 +1,6 @@
import collections
import qargparse
from avalon.pipeline import get_representation_context
from avalon.vendor import qargparse
from openpype.hosts.tvpaint.api import lib, pipeline, plugin

View file

@ -5,10 +5,10 @@ import uuid
import clique
from pymongo import UpdateOne
import ftrack_api
import qargparse
from Qt import QtWidgets, QtCore
from avalon import api, style
from avalon.vendor import qargparse
from avalon.api import AvalonMongoDB
import avalon.pipeline
from openpype.api import Anatomy

View file

@ -1,8 +1,8 @@
import logging
from Qt import QtWidgets, QtCore, QtGui
from avalon.vendor import qtawesome, qargparse
import qargparse
from avalon.vendor import qtawesome
from openpype.style import (
get_objected_colors,
get_style_image_path

View file

@ -0,0 +1,817 @@
"""
NOTE: The required `Qt` module has changed to use the one that vendorized.
Remember to change to relative import when updating this.
"""
import re
import logging
from collections import OrderedDict as odict
from Qt import QtCore, QtWidgets, QtGui
import qtawesome
__version__ = "0.5.2"
_log = logging.getLogger(__name__)
_type = type # used as argument
try:
# Python 2
_basestring = basestring
except NameError:
_basestring = str
class QArgumentParser(QtWidgets.QWidget):
"""User interface arguments
Arguments:
arguments (list, optional): Instances of QArgument
description (str, optional): Long-form text of what this parser is for
storage (QSettings, optional): Persistence to disk, providing
value() and setValue() methods
"""
changed = QtCore.Signal(QtCore.QObject) # A QArgument
def __init__(self,
arguments=None,
description=None,
storage=None,
parent=None):
super(QArgumentParser, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_StyledBackground)
# Create internal settings
if storage is True:
storage = QtCore.QSettings(
QtCore.QSettings.IniFormat,
QtCore.QSettings.UserScope,
__name__, "QArgparse",
)
if storage is not None:
_log.info("Storing settings @ %s" % storage.fileName())
arguments = arguments or []
assert hasattr(arguments, "__iter__"), "arguments must be iterable"
assert isinstance(storage, (type(None), QtCore.QSettings)), (
"storage must be of type QSettings"
)
layout = QtWidgets.QGridLayout(self)
layout.setRowStretch(999, 1)
if description:
layout.addWidget(QtWidgets.QLabel(description), 0, 0, 1, 2)
self._row = 1
self._storage = storage
self._arguments = odict()
self._desciption = description
for arg in arguments or []:
self._addArgument(arg)
self.setStyleSheet(style)
def setDescription(self, text):
self._desciption.setText(text)
def addArgument(self, name, type=None, default=None, **kwargs):
# Infer type from default
if type is None and default is not None:
type = _type(default)
# Default to string
type = type or str
Argument = {
None: String,
int: Integer,
float: Float,
bool: Boolean,
str: String,
list: Enum,
tuple: Enum,
}.get(type, type)
arg = Argument(name, default=default, **kwargs)
self._addArgument(arg)
return arg
def _addArgument(self, arg):
if arg["name"] in self._arguments:
raise ValueError("Duplicate argument '%s'" % arg["name"])
if self._storage is not None:
default = self._storage.value(arg["name"])
if default:
if isinstance(arg, Boolean):
default = bool({
None: QtCore.Qt.Unchecked,
0: QtCore.Qt.Unchecked,
1: QtCore.Qt.Checked,
2: QtCore.Qt.Checked,
"0": QtCore.Qt.Unchecked,
"1": QtCore.Qt.Checked,
"2": QtCore.Qt.Checked,
# May be stored as string, if used with IniFormat
"false": QtCore.Qt.Unchecked,
"true": QtCore.Qt.Checked,
}.get(default))
arg["default"] = default
arg.changed.connect(lambda: self.on_changed(arg))
label = (
QtWidgets.QLabel(arg["label"])
if arg.label
else QtWidgets.QLabel()
)
widget = arg.create()
icon = qtawesome.icon("fa.refresh", color="white")
reset = QtWidgets.QPushButton(icon, "") # default
reset.setToolTip("Reset")
reset.setProperty("type", "reset")
reset.clicked.connect(lambda: self.on_reset(arg))
# Shown on edit
reset.hide()
for widget in (label, widget):
widget.setToolTip(arg["help"])
widget.setObjectName(arg["name"]) # useful in CSS
widget.setProperty("type", type(arg).__name__)
widget.setAttribute(QtCore.Qt.WA_StyledBackground)
widget.setEnabled(arg["enabled"])
# Align label on top of row if widget is over two times heiger
height = (lambda w: w.sizeHint().height())
label_on_top = height(label) * 2 < height(widget)
alignment = (QtCore.Qt.AlignTop,) if label_on_top else ()
layout = self.layout()
layout.addWidget(label, self._row, 0, *alignment)
layout.addWidget(widget, self._row, 1)
layout.addWidget(reset, self._row, 2, *alignment)
layout.setColumnStretch(1, 1)
def on_changed(*_):
reset.setVisible(arg["edited"])
arg.changed.connect(on_changed)
self._row += 1
self._arguments[arg["name"]] = arg
def clear(self):
assert self._storage, "Cannot clear without persistent storage"
self._storage.clear()
_log.info("Clearing settings @ %s" % self._storage.fileName())
def find(self, name):
return self._arguments[name]
def on_reset(self, arg):
arg.write(arg["default"])
def on_changed(self, arg):
arg["edited"] = arg.read() != arg["default"]
self.changed.emit(arg)
# Optional PEP08 syntax
add_argument = addArgument
class QArgument(QtCore.QObject):
"""Base class of argument user interface
"""
changed = QtCore.Signal()
# Provide a left-hand side label for this argument
label = True
# For defining default value for each argument type
default = None
def __init__(self, name, default=None, **kwargs):
super(QArgument, self).__init__(kwargs.pop("parent", None))
kwargs["name"] = name
kwargs["label"] = kwargs.get("label", camel_to_title(name))
kwargs["default"] = self.default if default is None else default
kwargs["help"] = kwargs.get("help", "")
kwargs["read"] = kwargs.get("read")
kwargs["write"] = kwargs.get("write")
kwargs["enabled"] = bool(kwargs.get("enabled", True))
kwargs["edited"] = False
self._data = kwargs
def __str__(self):
return self["name"]
def __repr__(self):
return "%s(\"%s\")" % (type(self).__name__, self["name"])
def __getitem__(self, key):
return self._data[key]
def __setitem__(self, key, value):
self._data[key] = value
def __eq__(self, other):
if isinstance(other, _basestring):
return self["name"] == other
return super(QArgument, self).__eq__(other)
def __ne__(self, other):
return not self.__eq__(other)
def create(self):
return QtWidgets.QWidget()
def read(self):
return self._read()
def write(self, value):
self._write(value)
self.changed.emit()
class Boolean(QArgument):
"""Boolean type user interface
Presented by `QtWidgets.QCheckBox`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (bool, optional): Argument's default value, default None
enabled (bool, optional): Whether to enable this widget, default True
"""
def create(self):
widget = QtWidgets.QCheckBox()
widget.clicked.connect(self.changed.emit)
if isinstance(self, Tristate):
self._read = lambda: widget.checkState()
state = {
0: QtCore.Qt.Unchecked,
1: QtCore.Qt.PartiallyChecked,
2: QtCore.Qt.Checked,
"1": QtCore.Qt.PartiallyChecked,
"0": QtCore.Qt.Unchecked,
"2": QtCore.Qt.Checked,
}
else:
self._read = lambda: bool(widget.checkState())
state = {
None: QtCore.Qt.Unchecked,
0: QtCore.Qt.Unchecked,
1: QtCore.Qt.Checked,
2: QtCore.Qt.Checked,
"0": QtCore.Qt.Unchecked,
"1": QtCore.Qt.Checked,
"2": QtCore.Qt.Checked,
# May be stored as string, if used with QSettings(..IniFormat)
"false": QtCore.Qt.Unchecked,
"true": QtCore.Qt.Checked,
}
self._write = lambda value: widget.setCheckState(state[value])
widget.clicked.connect(self.changed.emit)
if self["default"] is not None:
self._write(self["default"])
return widget
def read(self):
return self._read()
class Tristate(QArgument):
"""Not implemented"""
class Number(QArgument):
"""Base class of numeric type user interface"""
default = 0
def create(self):
if isinstance(self, Float):
widget = QtWidgets.QDoubleSpinBox()
widget.setMinimum(self._data.get("min", 0.0))
widget.setMaximum(self._data.get("max", 99.99))
else:
widget = QtWidgets.QSpinBox()
widget.setMinimum(self._data.get("min", 0))
widget.setMaximum(self._data.get("max", 99))
widget.editingFinished.connect(self.changed.emit)
self._read = lambda: widget.value()
self._write = lambda value: widget.setValue(value)
if self["default"] != self.default:
self._write(self["default"])
return widget
class Integer(Number):
"""Integer type user interface
A subclass of `qargparse.Number`, presented by `QtWidgets.QSpinBox`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (int, optional): Argument's default value, default 0
min (int, optional): Argument's minimum value, default 0
max (int, optional): Argument's maximum value, default 99
enabled (bool, optional): Whether to enable this widget, default True
"""
class Float(Number):
"""Float type user interface
A subclass of `qargparse.Number`, presented by `QtWidgets.QDoubleSpinBox`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (float, optional): Argument's default value, default 0.0
min (float, optional): Argument's minimum value, default 0.0
max (float, optional): Argument's maximum value, default 99.99
enabled (bool, optional): Whether to enable this widget, default True
"""
class Range(Number):
"""Range type user interface
A subclass of `qargparse.Number`, not production ready.
"""
class Double3(QArgument):
"""Double3 type user interface
Presented by three `QtWidgets.QLineEdit` widget with `QDoubleValidator`
installed.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (tuple or list, optional): Default (0, 0, 0).
enabled (bool, optional): Whether to enable this widget, default True
"""
default = (0, 0, 0)
def create(self):
widget = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout(widget)
layout.setContentsMargins(0, 0, 0, 0)
x, y, z = (self.child_arg(layout, i) for i in range(3))
self._read = lambda: (
float(x.text()), float(y.text()), float(z.text()))
self._write = lambda value: [
w.setText(str(float(v))) for w, v in zip([x, y, z], value)]
if self["default"] != self.default:
self._write(self["default"])
return widget
def child_arg(self, layout, index):
widget = QtWidgets.QLineEdit()
widget.setValidator(QtGui.QDoubleValidator())
default = str(float(self["default"][index]))
widget.setText(default)
def focusOutEvent(event):
if not widget.text():
widget.setText(default) # Ensure value exists for `_read`
QtWidgets.QLineEdit.focusOutEvent(widget, event)
widget.focusOutEvent = focusOutEvent
widget.editingFinished.connect(self.changed.emit)
widget.returnPressed.connect(widget.editingFinished.emit)
layout.addWidget(widget)
return widget
class String(QArgument):
"""String type user interface
Presented by `QtWidgets.QLineEdit`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (str, optional): Argument's default value, default None
placeholder (str, optional): Placeholder message for the widget
enabled (bool, optional): Whether to enable this widget, default True
"""
def __init__(self, *args, **kwargs):
super(String, self).__init__(*args, **kwargs)
self._previous = None
def create(self):
widget = QtWidgets.QLineEdit()
widget.editingFinished.connect(self.onEditingFinished)
widget.returnPressed.connect(widget.editingFinished.emit)
self._read = lambda: widget.text()
self._write = lambda value: widget.setText(value)
if isinstance(self, Info):
widget.setReadOnly(True)
widget.setPlaceholderText(self._data.get("placeholder", ""))
if self["default"] is not None:
self._write(self["default"])
self._previous = self["default"]
return widget
def onEditingFinished(self):
current = self._read()
if current != self._previous:
self.changed.emit()
self._previous = current
class Info(String):
"""String type user interface but read-only
A subclass of `qargparse.String`, presented by `QtWidgets.QLineEdit`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (str, optional): Argument's default value, default None
enabled (bool, optional): Whether to enable this widget, default True
"""
class Color(String):
"""Color type user interface
A subclass of `qargparse.String`, not production ready.
"""
class Button(QArgument):
"""Button type user interface
Presented by `QtWidgets.QPushButton`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (bool, optional): Argument's default value, default None
enabled (bool, optional): Whether to enable this widget, default True
"""
label = False
def create(self):
widget = QtWidgets.QPushButton(self["label"])
widget.clicked.connect(self.changed.emit)
state = [
QtCore.Qt.Unchecked,
QtCore.Qt.Checked,
]
if isinstance(self, Toggle):
widget.setCheckable(True)
if hasattr(widget, "isChecked"):
self._read = lambda: state[int(widget.isChecked())]
self._write = (
lambda value: widget.setChecked(value)
)
else:
self._read = lambda: widget.checkState()
self._write = (
lambda value: widget.setCheckState(state[int(value)])
)
else:
self._read = lambda: "clicked"
self._write = lambda value: None
if self["default"] is not None:
self._write(self["default"])
return widget
class Toggle(Button):
"""Checkable `Button` type user interface
Presented by `QtWidgets.QPushButton`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
default (bool, optional): Argument's default value, default None
enabled (bool, optional): Whether to enable this widget, default True
"""
class InfoList(QArgument):
"""String list type user interface
Presented by `QtWidgets.QListView`, not production ready.
"""
def __init__(self, name, **kwargs):
kwargs["default"] = kwargs.pop("default", ["Empty"])
super(InfoList, self).__init__(name, **kwargs)
def create(self):
class Model(QtCore.QStringListModel):
def data(self, index, role):
return super(Model, self).data(index, role)
model = QtCore.QStringListModel(self["default"])
widget = QtWidgets.QListView()
widget.setModel(model)
widget.setEditTriggers(widget.NoEditTriggers)
self._read = lambda: model.stringList()
self._write = lambda value: model.setStringList(value)
return widget
class Choice(QArgument):
"""Argument user interface for selecting one from list
Presented by `QtWidgets.QListView`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
items (list, optional): List of strings for select, default `["Empty"]`
default (str, optional): Default item in `items`, use first of `items`
if not given.
enabled (bool, optional): Whether to enable this widget, default True
"""
def __init__(self, name, **kwargs):
kwargs["items"] = kwargs.get("items", ["Empty"])
kwargs["default"] = kwargs.pop("default", kwargs["items"][0])
super(Choice, self).__init__(name, **kwargs)
def index(self, value):
"""Return numerical equivalent to self.read()"""
return self["items"].index(value)
def create(self):
def on_changed(selected, deselected):
try:
selected = selected.indexes()[0]
except IndexError:
# At least one item must be selected at all times
selected = deselected.indexes()[0]
value = selected.data(QtCore.Qt.DisplayRole)
set_current(value)
self.changed.emit()
def set_current(current):
options = model.stringList()
if current == "Empty":
index = 0
else:
for index, member in enumerate(options):
if member == current:
break
else:
raise ValueError(
"%s not a member of %s" % (current, options)
)
qindex = model.index(index, 0, QtCore.QModelIndex())
smodel.setCurrentIndex(qindex, smodel.ClearAndSelect)
self["current"] = options[index]
def reset(items, default=None):
items = items or ["Empty"]
model.setStringList(items)
set_current(default or items[0])
model = QtCore.QStringListModel()
widget = QtWidgets.QListView()
widget.setModel(model)
widget.setEditTriggers(widget.NoEditTriggers)
widget.setSelectionMode(widget.SingleSelection)
smodel = widget.selectionModel()
smodel.selectionChanged.connect(on_changed)
self._read = lambda: self["current"]
self._write = lambda value: set_current(value)
self.reset = reset
reset(self["items"], self["default"])
return widget
class Separator(QArgument):
"""Visual separator
Example:
item1
item2
------------
item3
item4
"""
def create(self):
widget = QtWidgets.QWidget()
self._read = lambda: None
self._write = lambda value: None
return widget
class Enum(QArgument):
"""Argument user interface for selecting one from dropdown list
Presented by `QtWidgets.QComboBox`.
Arguments:
name (str): The name of argument
label (str, optional): Display name, convert from `name` if not given
help (str, optional): Tool tip message of this argument
items (list, optional): List of strings for select, default `[]`
default (int, optional): Index of default item, use first of `items`
if not given.
enabled (bool, optional): Whether to enable this widget, default True
"""
def __init__(self, name, **kwargs):
kwargs["default"] = kwargs.pop("default", 0)
kwargs["items"] = kwargs.get("items", [])
assert isinstance(kwargs["items"], (tuple, list)), (
"items must be list"
)
super(Enum, self).__init__(name, **kwargs)
def create(self):
widget = QtWidgets.QComboBox()
widget.addItems(self["items"])
widget.currentIndexChanged.connect(
lambda index: self.changed.emit())
self._read = lambda: widget.currentText()
self._write = lambda value: widget.setCurrentIndex(value)
if self["default"] is not None:
self._write(self["default"])
return widget
style = """\
QWidget {
/* Explicitly specify a size, to account for automatic HDPi */
font-size: 11px;
}
*[type="Button"] {
text-align:left;
}
*[type="Info"] {
background: transparent;
border: none;
}
QLabel[type="Separator"] {
min-height: 20px;
text-decoration: underline;
}
QPushButton[type="reset"] {
max-width: 11px;
max-height: 11px;
}
"""
def camelToTitle(text):
"""Convert camelCase `text` to Title Case
Example:
>>> camelToTitle("mixedCase")
"Mixed Case"
>>> camelToTitle("myName")
"My Name"
>>> camelToTitle("you")
"You"
>>> camelToTitle("You")
"You"
>>> camelToTitle("This is That")
"This Is That"
"""
return re.sub(
r"((?<=[a-z])[A-Z]|(?<!\A)[A-Z](?=[a-z]))",
r" \1", text
).title()
camel_to_title = camelToTitle
def _demo():
import sys
app = QtWidgets.QApplication(sys.argv)
parser = QArgumentParser()
parser.setWindowTitle("Demo")
parser.setMinimumWidth(300)
parser.add_argument("name", default="Marcus", help="Your name")
parser.add_argument("age", default=33, help="Your age")
parser.add_argument("height", default=1.87, help="Your height")
parser.add_argument("alive", default=True, help="Your state")
parser.add_argument("class", type=Enum, items=[
"Ranger",
"Warrior",
"Sorcerer",
"Monk",
], default=2, help="Your class")
parser.add_argument("options", type=Separator)
parser.add_argument("paths", type=InfoList, items=[
"Value A",
"Value B",
"Some other value",
"And finally, value C",
])
parser.add_argument("location", type=Double3)
parser.show()
app.exec_()
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--version", action="store_true")
parser.add_argument("--demo", action="store_true")
opts = parser.parse_args()
if opts.demo:
_demo()
if opts.version:
print(__version__)