From 396bdfde8a05d37be283f4ba411debc500ee4328 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 18 Jan 2022 17:16:32 +0100 Subject: [PATCH] added few new attribute definitions and their widgets --- openpype/pipeline/lib/__init__.py | 16 +- .../pipeline/lib/attribute_definitions.py | 125 +++- openpype/tools/resources/__init__.py | 45 ++ openpype/tools/resources/images/delete.png | Bin 0 -> 12343 bytes openpype/tools/resources/images/file.png | Bin 0 -> 2801 bytes openpype/tools/resources/images/files.png | Bin 0 -> 3094 bytes openpype/tools/resources/images/folder.png | Bin 0 -> 2627 bytes .../widgets/attribute_defs/files_widget.py | 645 ++++++++++++++++++ openpype/widgets/attribute_defs/widgets.py | 95 ++- 9 files changed, 919 insertions(+), 7 deletions(-) create mode 100644 openpype/tools/resources/__init__.py create mode 100644 openpype/tools/resources/images/delete.png create mode 100644 openpype/tools/resources/images/file.png create mode 100644 openpype/tools/resources/images/files.png create mode 100644 openpype/tools/resources/images/folder.png create mode 100644 openpype/widgets/attribute_defs/files_widget.py diff --git a/openpype/pipeline/lib/__init__.py b/openpype/pipeline/lib/__init__.py index 1bb65be79b..f762c4205d 100644 --- a/openpype/pipeline/lib/__init__.py +++ b/openpype/pipeline/lib/__init__.py @@ -1,18 +1,30 @@ from .attribute_definitions import ( AbtractAttrDef, + + UIDef, + UISeparatorDef, + UILabelDef, + UnknownDef, NumberDef, TextDef, EnumDef, - BoolDef + BoolDef, + FileDef, ) __all__ = ( "AbtractAttrDef", + + "UIDef", + "UISeparatorDef", + "UILabelDef", + "UnknownDef", "NumberDef", "TextDef", "EnumDef", - "BoolDef" + "BoolDef", + "FileDef", ) diff --git a/openpype/pipeline/lib/attribute_definitions.py b/openpype/pipeline/lib/attribute_definitions.py index 2b34e15bc4..111eb44429 100644 --- a/openpype/pipeline/lib/attribute_definitions.py +++ b/openpype/pipeline/lib/attribute_definitions.py @@ -38,13 +38,19 @@ class AbtractAttrDef: key(str): Under which key will be attribute value stored. label(str): Attribute label. tooltip(str): Attribute tooltip. + is_label_horizontal(bool): UI specific argument. Specify if label is + next to value input or ahead. """ + is_value_def = True - def __init__(self, key, default, label=None, tooltip=None): + def __init__( + self, key, default, label=None, tooltip=None, is_label_horizontal=None + ): self.key = key self.label = label self.tooltip = tooltip self.default = default + self.is_label_horizontal = is_label_horizontal self._id = uuid.uuid4() self.__init__class__ = AbtractAttrDef @@ -68,8 +74,39 @@ class AbtractAttrDef: pass +# ----------------------------------------- +# UI attribute definitoins won't hold value +# ----------------------------------------- + +class UIDef(AbtractAttrDef): + is_value_def = False + + def __init__(self, key=None, default=None, *args, **kwargs): + super(UIDef, self).__init__(key, default, *args, **kwargs) + + def convert_value(self, value): + return value + + +class UISeparatorDef(UIDef): + pass + + +class UILabelDef(UIDef): + def __init__(self, label): + super(UILabelDef, self).__init__(label=label) + + +# --------------------------------------- +# Attribute defintioins should hold value +# --------------------------------------- + class UnknownDef(AbtractAttrDef): - """Definition is not known because definition is not available.""" + """Definition is not known because definition is not available. + + This attribute can be used to keep existing data unchanged but does not + have known definition of type. + """ def __init__(self, key, default=None, **kwargs): kwargs["default"] = default super(UnknownDef, self).__init__(key, **kwargs) @@ -261,3 +298,87 @@ class BoolDef(AbtractAttrDef): if isinstance(value, bool): return value return self.default + + +class FileDef(AbtractAttrDef): + """File definition. + It is possible to define filters of allowed file extensions and if supports + folders. + Args: + multipath(bool): Allow multiple path. + folders(bool): Allow folder paths. + extensions(list): Allow files with extensions. Empty list will + allow all extensions and None will disable files completely. + default(str, list): Defautl value. + """ + + def __init__( + self, key, multipath=False, folders=None, extensions=None, + default=None, **kwargs + ): + if folders is None and extensions is None: + folders = True + extensions = [] + + if default is None: + if multipath: + default = [] + else: + default = "" + else: + if multipath: + if not isinstance(default, (tuple, list, set)): + raise TypeError(( + "'default' argument must be 'list', 'tuple' or 'set'" + ", not '{}'" + ).format(type(default))) + + else: + if not isinstance(default, six.string_types): + raise TypeError(( + "'default' argument must be 'str' not '{}'" + ).format(type(default))) + default = default.strip() + + # Change horizontal label + is_label_horizontal = kwargs.get("is_label_horizontal") + if is_label_horizontal is None and multipath: + kwargs["is_label_horizontal"] = False + + self.multipath = multipath + self.folders = folders + self.extensions = extensions + super(FileDef, self).__init__(key, default=default, **kwargs) + + def __eq__(self, other): + if not super(FileDef, self).__eq__(other): + return False + + return ( + self.multipath == other.multipath + and self.folders == other.folders + and self.extensions == other.extensions + ) + + def convert_value(self, value): + if isinstance(value, six.string_types): + if self.multipath: + value = [value.strip()] + else: + value = value.strip() + return value + + if isinstance(value, (tuple, list, set)): + _value = [] + for item in value: + if isinstance(item, six.string_types): + _value.append(item.strip()) + + if self.multipath: + return _value + + if not _value: + return self.default + return _value[0].strip() + + return str(value).strip() diff --git a/openpype/tools/resources/__init__.py b/openpype/tools/resources/__init__.py new file mode 100644 index 0000000000..fd5c45f901 --- /dev/null +++ b/openpype/tools/resources/__init__.py @@ -0,0 +1,45 @@ +import os + +from Qt import QtGui + + +def get_icon_path(icon_name=None, filename=None): + """Path to image in './images' folder.""" + if icon_name is None and filename is None: + return None + + if filename is None: + filename = "{}.png".format(icon_name) + + path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "images", + filename + ) + if os.path.exists(path): + return path + return None + + +def get_image(icon_name=None, filename=None): + """Load image from './images' as QImage.""" + path = get_icon_path(icon_name, filename) + if path: + return QtGui.QImage(path) + return None + + +def get_pixmap(icon_name=None, filename=None): + """Load image from './images' as QPixmap.""" + path = get_icon_path(icon_name, filename) + if path: + return QtGui.QPixmap(path) + return None + + +def get_icon(icon_name=None, filename=None): + """Load image from './images' as QICon.""" + pix = get_pixmap(icon_name, filename) + if pix: + return QtGui.QIcon(pix) + return None diff --git a/openpype/tools/resources/images/delete.png b/openpype/tools/resources/images/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..ab02768ba31460baedaaa4d965c6879e9781517e GIT binary patch literal 12343 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 literal 0 HcmV?d00001 diff --git a/openpype/tools/resources/images/file.png b/openpype/tools/resources/images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..4b05a0665f3f86f3adf98c65d87f14bb446b64c5 GIT binary patch literal 2801 zcmbtVX;@QN8a_9hm&kHuGg2rwaYsWI79m3*3sGQ*AfSM)NFWz-2S`E^ATXjK2De%o z85~Ee5YbwfRw+87)Psy6gjOh{qO>wp#`Y#y)KO=C%#Zt=@0|Dj_H(|w zK5^j!7rGA}0N@fE6P*MAi4aKuA`{EskIp|10Hp~}Ni(F$;|1B8frmK2cMs2p}11WN)3^7~JM3Dv};y z6c7dqFoOa$7UZk-0;7=Suqz;B`!R=wIw*!bAuCOuh)Oj&3=Ihk3FNXO>1c#bnIlMw zp8rgl*a=xGgF!3ca7s!_0!xAdHM(35FFZV)!{u}Md^W*g>r2%Jg^{h+PqmvEvk{Hy zvvs)EfNRvK-KHW-Q)CdbSOktfGmaVY=hoHwXXz7#;MfrkFObW5k#4|q-axk_uh6wB zjX|SVX|&@ByyRjWenpWe-|JP33hnF2a`}I!7ZkjdhTb46CJOPas4vCzn*)7HsTSiT zVR}uGE*p~-V`{@xM2#^0CX!@H_sU}~eOD3)rCS>s( zO7_|bVha?xSSqeGsGeJU4#Z>X+*c5LbooX0_AXZAL_T97IF!92*x zdm(VdC)X&6@PZuSJ%?hY(nOsm2hS%kebNF68Y`2A^1?$y+5EsU4@Bbxgf5;_8m%x8 zEIyadWApgzAZ`keF9_xfcp-DRJOP*Mu}T9Q8uP@I48`Fp?Xb@p+up>#JNP1g)DJS z4o;M_6eXSyo~y=mC@+x54Gex+sYHbl2H%_>=UH$Z`@{P!x(LoI@kclaFT7MjZ|sbi z0K_QajL#BcGd_(lH8Fy8#H<&UN|q$Fxj# zLEO%*?(+hVGAiFIxS&0J^2tX>OaFK~R(IeZ2WJ)bbWm48O&9)}6diZJChrdIWNpbi z1?Ouo>uWEK)DA`L81ijc6BM`P(2yu}ONSY%(?>RTq%S-M>rzVDrDO z_NJ^qEx*<(j!Ju{a>u@as>PpGUu$fAqaa(g_l zs!lXJY=>_J-73(tgst%G>*DXT_Hh6bd zzQ^t}rtDgJWY+bwpPrv~VuwUexuLe!@_VcPC2Ej_3JK`Z{<`~f9029^tSGvqJ*gKniGBLXZN{}k}fXy zI%LF_E~yy6vJPB~x-~fRT{=`oif{s-o02G*Hc{$?zLAeUC5^hqB~$0So>)>*OAfXq zt)fI&<_u;)Dmc~l$hj2ma69MIzSy*rcErne2y&Sa`G>ogk+^aXBP{!t;?wHil|)S= zM469dQt_rWiI!!%nKeJ8_BZ>`t_0bbD&Vve_WK&y&_2n95WKw2uz|wxvQ2NXfv?iR z+QgdbbXYcfYb8nwv((qT8v_Tli$x;=p6+IPa4y7HndCf| zvaO%AaV~{=tO4=tdcV>Jntq9VKvSQV7=}qrx6Q4)rkN^HXfDMmlNkdhib-j(dyB*h zyv>vbB-muJLDMKMsJ#+mat!PvlC%SNGtG)bgSAA`lu0ejb--0j(ZVc~6oGura2JvS z)(|OEyiCl!z*$Vqgad#y5n`C>Er=3SqU2eWDI#VYpo?jVFyADxLY`)48)5(!8yThe z0cIb7#S=uZ07%*)#7t!7F7lO-)=6VPA?$>@GNH4~X{Im>1zBqacM!gaA)&$2HlTo~ zpw3LPg{d@gEzZaqE7Xc4i999rb&?op5j-2EGpW(c-KJR60d6lSjpwfG_yt$%|c1DLTE6GWytGq{;8E!h>BQ;2o^;QtU-%xizEYjwm**5JcB zAh^Q=%}*+Z8C_>2;oM$R6Qk?${Eh*4@`@XK!63eCAmqN=-JZ==1A`A9LJ8-tl5LYN zEzfw{NV!t|y>sY;gx%RkT?fqIr`fJZ`+Y^}>YnfYs=v%*CJzKRl)CH*8@#s``Y^R( zL-z2g$*OBDKf<<~p#1H=(3U}vc?0yMi+s);Pfy6LxObr9!v%%kwi!~{t{@wM!f9qA6s19ld43E41dottCe{5v*@}~PsJV>iu^RJN?+AaV9 literal 0 HcmV?d00001 diff --git a/openpype/tools/resources/images/files.png b/openpype/tools/resources/images/files.png new file mode 100644 index 0000000000000000000000000000000000000000..667d6148ab75e01919bcee7dc9e59b2ed76d1fe1 GIT binary patch literal 3094 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 literal 0 HcmV?d00001 diff --git a/openpype/tools/resources/images/folder.png b/openpype/tools/resources/images/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..9c55e699278dee8f6f94cfc33dfd1db570f6e1f1 GIT binary patch literal 2627 zcmbtVd0Z3M7M@82C2U1SLHl$XvC5LnBp8*Hu$WK;35KvG7Ft3wkQ7KlCX|5Nh+3%n z>Q)7Xf@>}Cv*kUXOBG54tRgPBU_q#gf~b`WiYSWo-2@8y(D(j$`TZty&;7o0?z!K& z_smzK&_G+-6dD9Uwm}O8VhEyuAq9e|;C121$3H>Pn4_u)iC!WM;mS2BOqoIxk24J^ zT7W~4x357flPBSNBpy#xsd@BACo1WPO2MN?bA*^s%g2?fg_$}$JTo*xo|z=~QqX;U zXx;`c2#|v7Wr!gqSxs;aJi0k97mQ8QC>=3V^hrFrL?}Y|8Xb;sm>ecX_n{%)Iz<9k zEckfH8CX2JQm@x?Q8Yb0otf^*)aVjXmY0_oim_2Pn*kUMB15g085n9}hAG5g3;|Ba zbtyXh{(|5s2C?S zX*xL`@CB~c&oC=t(uFHh8SrF@K$U{437{^I&SA0t1C_oD_=WjlQ;!^sUfvEx6|V zgc=1%&(oaV2o%KUi*%X!Md&rU4D;0BQ4hyc z2dzOfS!F)s2x6`@S0*=!#G`9<8bz8MSC|(E;RaDclb}zR>2Uu19RRcvbhwuQ zW4IgPYS4mopx1O)ZQTVyaFz;GW>iHWX!ho`%@^GTVG~dLdh%;R_KGH5-Y1jigiV;b zeuoon{f@8|DG7yryYHUwoS3aH{=?9 zHzdhl6I=V)`!7!g2iJ|Ot4mG1Y|-A~b9d3h?uQ|g_ydNIxF2LaPi38)4KK67JPvLA zw7jQwom=0ln|)W>T%tEi_vie=4idL@=PcRuAoVM&OLy3pC!Qbpv$M3&sb_m>^DEKI zI43k#bQCU{S#7vEZ^K1u*>Ba-BU_4^SMQF#w8-&%h4lr_e6Jss|6t6I%|7xGrzD_q zX8tkGX0l_3_MHEdnR7h8S=xK(Vk<)s)n3YsTRQf5dpXfLqTWDqOVJ_ z_bfy+EN!HeMKV?I?rdj4tLq2C0RQ|J%IJ^Y0BdGWy`zkqMr@UK7h z*6ys^)bAXQt$h_tthz$|ew$T66lvdIuxhPUqbKIMitwF$EYMg(y^#O?)icx>cWvk@ zG1WfjMXmJ4qxJDM7PmPr_SJR*uZT8}M}Fm2A;u5MwbYsu3oksjb9o{4`+Rb7br!9O~l47!gCdnhy(^k5Qw-=?{O*>H(<8aHjYq4NF)EWER zp`f#mC*;Uyz*%iK?<|T$46T9vqG-y-n!;jbY^ia4%-lAQXVj{EMuwA}-`L#y(x}S( z8_v7ztKl`k_?foLo^QD;vCob4vGF1c+S!{Ju@=eBP9>x}wa9BLg>QT2RonFa?tXVm z+rtTWEQ&mrn;-3nJn6pQaGmCXBTwKz8Y18jTc;&(B%JJ6f6yqljB(7X^GkEO(OeI0 zz;|(N9xXpdc`6f~Cz;^Yc-qzaYPQUNfi;#3U!gU+VTUPlXKiHMZ0b{wdDIe{tJ$rN zr@Z~n=_Zs-f^TDO;EA!ybHhHf0o-kHIuF|pUq|`y>`2eD${Wa7^+Dra$LIa${b_ko z#^rx&4F9g(*!jD$!qLzJUqdNVhl~IJ4C*J;mk&Ij-Z7QD+PYfcx72u+`iDbV7x~Fb zGV|vbC;Is=y^Q^ZKMpXTO)XpjJvH>C?-&=CAnhYtXSvS5;F5|Y9adYBhu1M7kChI+ zU$e$MZ?M&h;~rSNXZNq*Ut6q^f}CwX?4PX7r6i%Gc3;$SJsP)_(u~F_Uh#d6XUT4; zz$VYmZ@vxT3;ychafiyMk=VKy(3-FoA98b#{j|85#(QumC1e^xt9l6UK|H1**8@A@ zJ 0 + self._files_view.setVisible(files_exists) + self._empty_widget.setVisible(not files_exists) + + +class SingleFileWidget(QtWidgets.QWidget): + value_changed = QtCore.Signal() + + def __init__(self, parent): + super(SingleFileWidget, self).__init__(parent) + + self.setAcceptDrops(True) + + filepath_input = QtWidgets.QLineEdit(self) + + browse_btn = QtWidgets.QPushButton("Browse", self) + browse_btn.setVisible(False) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(filepath_input, 1) + layout.addWidget(browse_btn, 0) + + browse_btn.clicked.connect(self._on_browse_clicked) + filepath_input.textChanged.connect(self._on_text_change) + + self._in_set_value = False + + self._filepath_input = filepath_input + self._folders_allowed = False + self._exts_filter = [] + + def set_value(self, value, multivalue): + self._in_set_value = True + + if multivalue: + set_value = set(value) + if len(set_value) == 1: + value = tuple(set_value)[0] + else: + value = "< Multiselection >" + self._filepath_input.setText(value) + + self._in_set_value = False + + def current_value(self): + return self._filepath_input.text() + + def set_filters(self, folders_allowed, exts_filter): + self._folders_allowed = folders_allowed + self._exts_filter = exts_filter + + def _on_text_change(self, text): + if not self._in_set_value: + self.value_changed.emit() + + def _on_browse_clicked(self): + # TODO implement file dialog logic in '_on_browse_clicked' + print("_on_browse_clicked") + + def dragEnterEvent(self, event): + mime_data = event.mimeData() + if not mime_data.hasUrls(): + return + + filepaths = [] + for url in mime_data.urls(): + filepath = url.toLocalFile() + if os.path.exists(filepath): + filepaths.append(filepath) + + # TODO add folder, extensions check + if len(filepaths) == 1: + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() + + def dragLeaveEvent(self, event): + event.accept() + + def dropEvent(self, event): + mime_data = event.mimeData() + if mime_data.hasUrls(): + filepaths = [] + for url in mime_data.urls(): + filepath = url.toLocalFile() + if os.path.exists(filepath): + filepaths.append(filepath) + # TODO filter check + if len(filepaths) == 1: + self.set_value(filepaths[0], False) + event.accept() diff --git a/openpype/widgets/attribute_defs/widgets.py b/openpype/widgets/attribute_defs/widgets.py index 1cfed08363..2eb22209db 100644 --- a/openpype/widgets/attribute_defs/widgets.py +++ b/openpype/widgets/attribute_defs/widgets.py @@ -1,14 +1,20 @@ +import os import uuid + +from Qt import QtWidgets, QtCore + from openpype.pipeline.lib import ( AbtractAttrDef, UnknownDef, NumberDef, TextDef, EnumDef, - BoolDef + BoolDef, + FileDef, + UISeparatorDef, + UILabelDef ) from openpype.widgets.nice_checkbox import NiceCheckbox -from Qt import QtWidgets, QtCore def create_widget_for_attr_def(attr_def, parent=None): @@ -32,12 +38,22 @@ def create_widget_for_attr_def(attr_def, parent=None): if isinstance(attr_def, UnknownDef): return UnknownAttrWidget(attr_def, parent) + if isinstance(attr_def, FileDef): + return FileAttrWidget(attr_def, parent) + + if isinstance(attr_def, UISeparatorDef): + return SeparatorAttrWidget(attr_def, parent) + + if isinstance(attr_def, UILabelDef): + return LabelAttrWidget(attr_def, parent) + raise ValueError("Unknown attribute definition \"{}\"".format( str(type(attr_def)) )) class _BaseAttrDefWidget(QtWidgets.QWidget): + # Type 'object' may not work with older PySide versions value_changed = QtCore.Signal(object, uuid.UUID) def __init__(self, attr_def, parent): @@ -68,12 +84,36 @@ class _BaseAttrDefWidget(QtWidgets.QWidget): def set_value(self, value, multivalue=False): raise NotImplementedError( - "Method 'current_value' is not implemented. {}".format( + "Method 'set_value' is not implemented. {}".format( self.__class__.__name__ ) ) +class SeparatorAttrWidget(_BaseAttrDefWidget): + def _ui_init(self): + input_widget = QtWidgets.QWidget(self) + input_widget.setObjectName("Separator") + input_widget.setMinimumHeight(2) + input_widget.setMaximumHeight(2) + + self._input_widget = input_widget + + self.main_layout.addWidget(input_widget, 0) + + +class LabelAttrWidget(_BaseAttrDefWidget): + def _ui_init(self): + input_widget = QtWidgets.QLabel(self) + label = self.attr_def.label + if label: + input_widget.setText(str(label)) + + self._input_widget = input_widget + + self.main_layout.addWidget(input_widget, 0) + + class NumberAttrWidget(_BaseAttrDefWidget): def _ui_init(self): decimals = self.attr_def.decimals @@ -83,6 +123,9 @@ class NumberAttrWidget(_BaseAttrDefWidget): else: input_widget = QtWidgets.QSpinBox(self) + if self.attr_def.tooltip: + input_widget.setToolTip(self.attr_def.tooltip) + input_widget.setMinimum(self.attr_def.minimum) input_widget.setMaximum(self.attr_def.maximum) input_widget.setValue(self.attr_def.default) @@ -136,6 +179,9 @@ class TextAttrWidget(_BaseAttrDefWidget): ): input_widget.setPlaceholderText(self.attr_def.placeholder) + if self.attr_def.tooltip: + input_widget.setToolTip(self.attr_def.tooltip) + if self.attr_def.default: if self.multiline: input_widget.setPlainText(self.attr_def.default) @@ -184,6 +230,9 @@ class BoolAttrWidget(_BaseAttrDefWidget): input_widget = NiceCheckbox(parent=self) input_widget.setChecked(self.attr_def.default) + if self.attr_def.tooltip: + input_widget.setToolTip(self.attr_def.tooltip) + input_widget.stateChanged.connect(self._on_value_change) self._input_widget = input_widget @@ -220,6 +269,9 @@ class EnumAttrWidget(_BaseAttrDefWidget): combo_delegate = QtWidgets.QStyledItemDelegate(input_widget) input_widget.setItemDelegate(combo_delegate) + if self.attr_def.tooltip: + input_widget.setToolTip(self.attr_def.tooltip) + items = self.attr_def.items for key, label in items.items(): input_widget.addItem(label, key) @@ -281,3 +333,40 @@ class UnknownAttrWidget(_BaseAttrDefWidget): if str_value != self._value: self._value = str_value self._input_widget.setText(str_value) + + +class FileAttrWidget(_BaseAttrDefWidget): + def _ui_init(self): + self.multipath = self.attr_def.multipath + if self.multipath: + from .files_widget import MultiFilesWidget + + input_widget = MultiFilesWidget(self) + + else: + from .files_widget import SingleFileWidget + + input_widget = SingleFileWidget(self) + + if self.attr_def.tooltip: + input_widget.setToolTip(self.attr_def.tooltip) + + input_widget.set_filters( + self.attr_def.folders, self.attr_def.extensions + ) + + input_widget.value_changed.connect(self._on_value_change) + + self._input_widget = input_widget + + self.main_layout.addWidget(input_widget, 0) + + def _on_value_change(self): + new_value = self.current_value() + self.value_changed.emit(new_value, self.attr_def.id) + + def current_value(self): + return self._input_widget.current_value() + + def set_value(self, value, multivalue=False): + self._input_widget.set_value(value, multivalue)