Skip to content

Commit cef2880

Browse files
committed
Merge branch 'master' of https://github.com/getavalon/core into fix412
# Conflicts: # avalon/tools/workfiles/app.py Note this changes the fix from getavalon#465 to not store the widgets as explicit variables but by setting the parent widget
2 parents 7b8499d + 8369285 commit cef2880

File tree

34 files changed

+2064
-302
lines changed

34 files changed

+2064
-302
lines changed

avalon/fusion/__init__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
)
1919

2020
from .workio import (
21-
open,
22-
save,
21+
open_file,
22+
save_file,
2323
current_file,
2424
has_unsaved_changes,
2525
file_extensions,
@@ -42,8 +42,8 @@
4242
"comp_lock_and_undo_chunk",
4343

4444
# Workfiles API
45-
"open",
46-
"save",
45+
"open_file",
46+
"save_file",
4747
"current_file",
4848
"has_unsaved_changes",
4949
"file_extensions",
@@ -52,3 +52,7 @@
5252
"maintained_selection"
5353

5454
]
55+
56+
# Backwards API compatibility
57+
open = open_file
58+
save = save_file

avalon/fusion/workio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ def has_unsaved_changes():
1414
return comp.GetAttrs()["COMPB_Modified"]
1515

1616

17-
def save(filepath):
17+
def save_file(filepath):
1818
from avalon.fusion.pipeline import get_current_comp
1919

2020
comp = get_current_comp()
2121
comp.Save(filepath)
2222

2323

24-
def open(filepath):
24+
def open_file(filepath):
2525
# Hack to get fusion, see avalon.fusion.pipeline.get_current_comp()
2626
fusion = getattr(sys.modules["__main__"], "fusion", None)
2727

avalon/houdini/__init__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
)
1111

1212
from .workio import (
13-
open,
14-
save,
13+
open_file,
14+
save_file,
1515
current_file,
1616
has_unsaved_changes,
1717
file_extensions,
@@ -38,8 +38,8 @@
3838
"containerise",
3939

4040
# Workfiles API
41-
"open",
42-
"save",
41+
"open_file",
42+
"save_file",
4343
"current_file",
4444
"has_unsaved_changes",
4545
"file_extensions",
@@ -53,3 +53,7 @@
5353
"maintained_selection",
5454
"unique_name"
5555
]
56+
57+
# Backwards API compatibility
58+
open = open_file
59+
save = save_file

avalon/houdini/pipeline.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ def find_host_config(config):
7575
return config
7676

7777

78+
def get_main_window():
79+
"""Acquire Houdini's main window"""
80+
if self._parent is None:
81+
self._parent = hou.ui.mainQtWindow()
82+
return self._parent
83+
84+
7885
def reload_pipeline(*args):
7986
"""Attempt to reload pipeline at run-time.
8087
@@ -116,7 +123,7 @@ def reload_pipeline(*args):
116123
module = importlib.import_module(module)
117124
reload(module)
118125

119-
self._parent = {hou.ui.mainQtWindow().objectName(): hou.ui.mainQtWindow()}
126+
get_main_window()
120127

121128
import avalon.houdini
122129
api.install(avalon.houdini)
@@ -201,12 +208,11 @@ def containerise(name,
201208
return container
202209

203210

204-
def parse_container(container, validate=True):
211+
def parse_container(container):
205212
"""Return the container node's full container data.
206213
207214
Args:
208215
container (hou.Node): A container node name.
209-
validate(bool): turn the validation for the container on or off
210216
211217
Returns:
212218
dict: The container schema data for this container node.
@@ -221,9 +227,6 @@ def parse_container(container, validate=True):
221227
data["objectName"] = container.path()
222228
data["node"] = container
223229

224-
if validate:
225-
schema.validate(data)
226-
227230
return data
228231

229232

@@ -321,9 +324,7 @@ def on_file_event_callback(event):
321324

322325

323326
def on_houdini_initialize():
324-
325-
main_window = hou.qt.mainWindow()
326-
self._parent = {main_window.objectName(): main_window}
327+
get_main_window()
327328

328329

329330
def _register_callbacks():

avalon/houdini/workio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def has_unsaved_changes():
1212
return hou.hipFile.hasUnsavedChanges()
1313

1414

15-
def save(filepath):
15+
def save_file(filepath):
1616

1717
# Force forwards slashes to avoid segfault
1818
filepath = filepath.replace("\\", "/")
@@ -23,7 +23,7 @@ def save(filepath):
2323
return filepath
2424

2525

26-
def open(filepath):
26+
def open_file(filepath):
2727

2828
# Force forwards slashes to avoid segfault
2929
filepath = filepath.replace("\\", "/")

avalon/lib.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,13 @@ def launch(executable, args=None, environment=None, cwd=None):
217217
universal_newlines=True,
218218
)
219219

220-
if env.get("CREATE_NEW_CONSOLE"):
220+
# this won't do anything on linux/macos as `creationFlags` are
221+
# only windows specific.
222+
if IS_WIN32 and env.get("CREATE_NEW_CONSOLE"):
221223
kwargs["creationflags"] = CREATE_NEW_CONSOLE
222224
kwargs.pop("stdout")
223225
kwargs.pop("stderr")
224226
else:
225-
226227
if IS_WIN32:
227228
kwargs["creationflags"] = CREATE_NO_WINDOW
228229

avalon/maya/__init__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
)
2828

2929
from .workio import (
30-
open,
31-
save,
30+
open_file,
31+
save_file,
3232
current_file,
3333
has_unsaved_changes,
3434
file_extensions,
@@ -71,8 +71,8 @@
7171
"lock_ignored",
7272

7373
# Workfiles API
74-
"open",
75-
"save",
74+
"open_file",
75+
"save_file",
7676
"current_file",
7777
"has_unsaved_changes",
7878
"file_extensions",
@@ -93,3 +93,7 @@
9393
"suspended_refresh",
9494

9595
]
96+
97+
# Backwards API compatibility
98+
open = open_file
99+
save = save_file

avalon/maya/commands.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,22 @@ def reset_frame_range():
3333
cmds.warning("No edit information found for %s" % shot["name"])
3434
return
3535

36-
fps = {
37-
"12": "12fps",
38-
"15": "game",
39-
"16": "16fps",
40-
"24": "film",
41-
"25": "pal",
42-
"30": "ntsc",
43-
"48": "show",
44-
"50": "palf",
45-
"60": "ntscf"
46-
}.get(api.Session.get("AVALON_FPS"), "pal") # Default to "pal"
36+
fps = {15: 'game',
37+
24: 'film',
38+
25: 'pal',
39+
30: 'ntsc',
40+
48: 'show',
41+
50: 'palf',
42+
60: 'ntscf',
43+
23.98: '23.976fps',
44+
23.976: '23.976fps',
45+
29.97: '29.97fps',
46+
47.952: '47.952fps',
47+
47.95: '47.952fps',
48+
59.94: '59.94fps',
49+
44100: '44100fps',
50+
48000: '48000fps'
51+
}.get(float(api.Session.get("AVALON_FPS", 25)), "pal")
4752

4853
cmds.currentUnit(time=fps)
4954

avalon/maya/pipeline.py

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import contextlib
66

77
from maya import cmds, OpenMaya
8+
import maya.utils
9+
import maya.api.OpenMaya as om
810
from pyblish import api as pyblish
911

1012
from . import lib, compat
@@ -93,6 +95,16 @@ def find_host_config(config):
9395
return config
9496

9597

98+
def get_main_window():
99+
"""Acquire Maya's main window"""
100+
if self._parent is None:
101+
self._parent = {
102+
widget.objectName(): widget
103+
for widget in QtWidgets.QApplication.topLevelWidgets()
104+
}["MayaWindow"]
105+
return self._parent
106+
107+
96108
def uninstall(config):
97109
"""Uninstall Maya-specific functionality of avalon-core.
98110
@@ -194,15 +206,20 @@ def deferred():
194206
command=interactive.reset_resolution)
195207

196208
# Allow time for uninstallation to finish.
197-
QtCore.QTimer.singleShot(100, deferred)
209+
# We use Maya's executeDeferred instead of QTimer.singleShot
210+
# so that it only gets called after Maya UI has initialized too.
211+
# This is crucial with Maya 2020+ which initializes without UI
212+
# first as a QCoreApplication
213+
maya.utils.executeDeferred(deferred)
198214

199215

200216
def launch_workfiles_app(*args):
201217
workfiles.show(
202218
os.path.join(
203219
cmds.workspace(query=True, rootDirectory=True),
204220
cmds.workspace(fileRuleEntry="scene")
205-
)
221+
),
222+
parent=self._parent
206223
)
207224

208225

@@ -248,18 +265,21 @@ def reload_pipeline(*args):
248265
module = importlib.import_module(module)
249266
reload(module)
250267

251-
self._parent = {
252-
widget.objectName(): widget
253-
for widget in QtWidgets.QApplication.topLevelWidgets()
254-
}["MayaWindow"]
268+
get_main_window()
255269

256270
import avalon.maya
257271
api.install(avalon.maya)
258272

259273

260274
def _uninstall_menu():
261-
app = QtWidgets.QApplication.instance()
262-
widgets = dict((w.objectName(), w) for w in app.allWidgets())
275+
276+
# In Maya 2020+ don't use the QApplication.instance()
277+
# during startup (userSetup.py) as it will return a
278+
# QtCore.QCoreApplication instance which does not have
279+
# the allWidgets method. As such, we call the staticmethod.
280+
all_widgets = QtWidgets.QApplication.allWidgets()
281+
282+
widgets = dict((w.objectName(), w) for w in all_widgets)
263283
menu = widgets.get(self._menu)
264284

265285
if menu:
@@ -422,7 +442,7 @@ def containerise(name,
422442
return container
423443

424444

425-
def parse_container(container, validate=True):
445+
def parse_container(container):
426446
"""Return the container node's full container data.
427447
428448
Args:
@@ -440,28 +460,59 @@ def parse_container(container, validate=True):
440460
# Append transient data
441461
data["objectName"] = container
442462

443-
if validate:
444-
schema.validate(data)
445-
446463
return data
447464

448465

449466
def _ls():
450-
containers = list()
451-
for identifier in (AVALON_CONTAINER_ID,
452-
"pyblish.mindbender.container"):
453-
containers += lib.lsattr("id", identifier)
467+
"""Yields Avalon container node names.
454468
455-
return containers
469+
Used by `ls()` to retrieve the nodes and then query the full container's
470+
data.
471+
472+
Yields:
473+
str: Avalon container node name (objectSet)
474+
475+
"""
476+
477+
def _maya_iterate(iterator):
478+
"""Helper to iterate a maya iterator"""
479+
while not iterator.isDone():
480+
yield iterator.thisNode()
481+
iterator.next()
482+
483+
ids = {AVALON_CONTAINER_ID,
484+
# Backwards compatibility
485+
"pyblish.mindbender.container"}
486+
487+
# Iterate over all 'set' nodes in the scene to detect whether
488+
# they have the avalon container ".id" attribute.
489+
fn_dep = om.MFnDependencyNode()
490+
iterator = om.MItDependencyNodes(om.MFn.kSet)
491+
for mobject in _maya_iterate(iterator):
492+
if mobject.apiTypeStr != "kSet":
493+
# Only match by exact type
494+
continue
495+
496+
fn_dep.setObject(mobject)
497+
if not fn_dep.hasAttribute("id"):
498+
continue
499+
500+
plug = fn_dep.findPlug("id", True)
501+
value = plug.asString()
502+
if value in ids:
503+
yield fn_dep.name()
456504

457505

458506
def ls():
459-
"""List containers from active Maya scene
507+
"""Yields containers from active Maya scene
460508
461509
This is the host-equivalent of api.ls(), but instead of listing
462510
assets on disk, it lists assets already loaded in Maya; once loaded
463511
they are called 'containers'
464512
513+
Yields:
514+
dict: container
515+
465516
"""
466517
container_names = _ls()
467518

@@ -592,10 +643,7 @@ def _on_maya_initialized(*args):
592643
return
593644

594645
# Keep reference to the main Window, once a main window exists.
595-
self._parent = {
596-
widget.objectName(): widget
597-
for widget in QtWidgets.QApplication.topLevelWidgets()
598-
}["MayaWindow"]
646+
get_main_window()
599647

600648

601649
def _on_scene_new(*args):

0 commit comments

Comments
 (0)