Skip to content

Latest commit

 

History

History
2179 lines (1704 loc) · 89.3 KB

File metadata and controls

2179 lines (1704 loc) · 89.3 KB

Qt5 to Qt6 deprecation warnings and runtime error in MayaEditor dialog

Session ID: ses_1f7e1178dffeBFBTZQlN7hP426 Created: 5/8/2026, 4:05:03 PM Updated: 5/8/2026, 5:09:53 PM


User

/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:284: PySideDeprecationWarningRemovedInQt6:

The "+" operator is deprecated in Qt For Python 6.0 .

Please use "|" instead.

show_output_window_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_1))

# /Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:295: PySideDeprecationWarningRemovedInQt6:

The "+" operator is deprecated in Qt For Python 6.0 .

Please use "|" instead.

show_sidebar_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_0))

Error: RuntimeError: AttributeError: file /Applications/Autodesk/maya2026/Maya.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/enum.py line 786: 0


Assistant (Build · Big Pickle · 4.5s)

Thinking:

The user is showing deprecation warnings from PySide6 about using + instead of | for combining Qt modifier flags. They also show a RuntimeError with an AttributeError about a zero value in an enum. Let me look at the relevant file to fix this.

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "offset": 280,
  "limit": 30
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py</path>
<type>file</type>
<content>
280:             lambda state: self.ui.output_window_group_box.setVisible(state)
281:         )
282:         show_output_window_action.setCheckable(True)
283:         show_output_window_action.setChecked(True)
284:         show_output_window_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_1))
285: 
286:         # show sidebar window
287:         show_sidebar_action = QAction("Show Sidebar", self)
288:         settings_menu.addAction(show_sidebar_action)
289:         show_sidebar_action.toggled.connect(
290:             lambda state: self.ui.side_bar.setVisible(state)
291:         )
292:         show_sidebar_action.setCheckable(True)
293:         show_sidebar_action.setChecked(True)
294: 
295:         show_sidebar_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_0))
296: 
297:         # Menu to main menu bar
298:         self.menu_bar.addMenu(settings_menu)
299: 
300:         self.ui.main_grid_layout.setMenuBar(self.menu_bar)  # type: ignore
301: 
302:     def open_file(self) -> None:
303:         """Open a new file and add to the tabs."""
304:         file_name, _ = QFileDialog.getOpenFileName(
305:             self,
306:             "Select File to Open",
307:             "",
308:             ("Mel / Python (*.py *.mel, *.*)"),
309:         )

(Showing lines 280-309 of 687. Use offset=310 to continue.)
</content>

Assistant (Build · Big Pickle · 6.0s)

Thinking:

The issue is clear: Qt.CTRL + Qt.Key_1 should be Qt.CTRL | Qt.Key_1. The + operator is deprecated in PySide6 for combining Qt modifier flags. Let me also check if there are more occurrences across the codebase.

Two issues: + should be | for combining Qt flags, and Qt.CTRL should be Qt.KeyboardModifier.ControlModifier (the old Qt.CTRL constant is 0 in Qt6 enums, causing the AttributeError). Let me check the codebase for all occurrences.

Tool: grep

Input:

{
  "pattern": "Qt\\.CTRL\\s*\\+",
  "path": "/Volumes/teaching/Code/MayaEditor"
}

Output:

Found 4 matches
/Volumes/teaching/Code/MayaEditor/ConvertToPySide6.md:
  Line 8166: 284:         show_output_window_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_1))

  Line 8177: 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_0))


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:
  Line 284:         show_output_window_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_1))

  Line 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_0))

Tool: grep

Input:

{
  "pattern": "Qt\\.CTRL",
  "path": "/Volumes/teaching/Code/MayaEditor"
}

Output:

Found 4 matches
/Volumes/teaching/Code/MayaEditor/ConvertToPySide6.md:
  Line 8166: 284:         show_output_window_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_1))

  Line 8177: 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_0))


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:
  Line 284:         show_output_window_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_1))

  Line 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_0))


Assistant (Build · Big Pickle · 10.3s)

Thinking:

Only two occurrences in the actual source file (the .md file is a reference doc). The fix is to change Qt.CTRL + Qt.Key_X to Qt.KeyboardModifier.ControlModifier | Qt.Key_X. In PySide6, the old Qt.CTRL constant (which was an int alias) has been removed/changed, and the proper way is to use Qt.KeyboardModifier.ControlModifier.

Actually, let me think about this more carefully. In PySide6:

  • Qt.CTRL was deprecated and may now be 0 or invalid
  • The correct modifier is Qt.KeyboardModifier.ControlModifier
  • And the | operator should be used instead of +

So the fix is:

  • Qt.CTRLQt.KeyboardModifier.ControlModifier
  • +|

But wait, QKeySequence can take a string or a QKeyCombination. Let me check if Qt.KeyboardModifier.ControlModifier | Qt.Key_1 works correctly with QKeySequence. In PySide6, QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1) should work.

Actually, a cleaner approach would be to use QKeySequence("Ctrl+1") or QKeySequence(Qt.CTRL | Qt.Key_1). But since Qt.CTRL is the deprecated constant, the modern approach is:

QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1)

Let me apply the fix.

Tool: edit

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "oldString": "Qt.CTRL + Qt.Key_1",
  "newString": "Qt.KeyboardModifier.ControlModifier | Qt.Key_1"
}

Output:

Edit applied successfully.

Tool: edit

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "oldString": "Qt.CTRL + Qt.Key_0",
  "newString": "Qt.KeyboardModifier.ControlModifier | Qt.Key_0"
}

Output:

Edit applied successfully.

Assistant (Build · Big Pickle · 3.2s)

Thinking:

The edits have been applied. Let me verify the changes.

Tool: read

Input:

{
  "offset": 280,
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "limit": 20
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py</path>
<type>file</type>
<content>
280:             lambda state: self.ui.output_window_group_box.setVisible(state)
281:         )
282:         show_output_window_action.setCheckable(True)
283:         show_output_window_action.setChecked(True)
284:         show_output_window_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1))
285: 
286:         # show sidebar window
287:         show_sidebar_action = QAction("Show Sidebar", self)
288:         settings_menu.addAction(show_sidebar_action)
289:         show_sidebar_action.toggled.connect(
290:             lambda state: self.ui.side_bar.setVisible(state)
291:         )
292:         show_sidebar_action.setCheckable(True)
293:         show_sidebar_action.setChecked(True)
294: 
295:         show_sidebar_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_0))
296: 
297:         # Menu to main menu bar
298:         self.menu_bar.addMenu(settings_menu)
299: 

(Showing lines 280-299 of 687. Use offset=300 to continue.)
</content>

Assistant (Build · Big Pickle · 3.4s)

Fixed both deprecation warnings. Changed Qt.CTRL + Qt.Key_XQt.KeyboardModifier.ControlModifier | Qt.Key_X at EditorDialog.py:284 and :295. The + operator is replaced with | (bitwise OR) for flag combining, and Qt.CTRL (which resolves to 0 in Qt6 enums) is replaced with the proper enum value Qt.KeyboardModifier.ControlModifier.


User

// Error: RuntimeError: file /Users/jmacey/teaching/Code/MayaEditor/plug-ins/MayaEditor.py line 31: No module path found // Warning: file: /Applications/Autodesk/maya2026/Maya.app/Contents/scripts/others/pluginWin.mel line 934: Failed to run file: /Users/jmacey/teaching/Code/MayaEditor/plug-ins/MayaEditor.py // Error: file: /Applications/Autodesk/maya2026/Maya.app/Contents/scripts/others/pluginWin.mel line 934: (MayaEditor)


Assistant (Build · Big Pickle · 4.2s)

Thinking:

The user is getting an error when trying to load the MayaEditor plugin in Maya 2026. The error says "No module path found" at line 31 of plug-ins/MayaEditor.py. Let me look at that file to understand the issue.

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditor.py"
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditor.py</path>
<type>file</type>
<content>
1: # Copyright (C) 2022  Jonathan Macey
2: #
3: # This program is free software: you can redistribute it and/or modify
4: # it under the terms of the GNU General Public License as published by
5: # the Free Software Foundation, either version 3 of the License, or
6: #  any later version.
7: #
8: #  This program is distributed in the hope that it will be useful,
9: #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10: #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11: #  GNU General Public License for more details.
12: #
13: #  You should have received a copy of the GNU General Public License
14: #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
15: import os
16: import sys
17: from builtins import int
18: 
19: import maya.api.OpenMaya as OpenMaya
20: import maya.api.OpenMayaUI as OpenMayaUI
21: import maya.cmds as cmds
22: import maya.mel as mel
23: from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
24: from PySide6 import QtCore, QtWidgets
25: from PySide6.QtCore import QFile
26: from PySide6.QtGui import QColor, QFont
27: from PySide6.QtUiTools import QUiLoader
28: from shiboken6 import wrapInstance  # type: ignore
29: 
30: # Grab the module root so we can append our python path
31: root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")
32: try:
33:     import importlib
34: 
35:     # for debug purposed it is easier to re-load module
36:     # so append to path then try to delete if exists.
37:     sys.path.insert(0, root_path + "/plug-ins")
38:     try:
39:         print("deleting MayaEditorCore")
40:         del sys.modules["MayaEditorCore"]
41:         import MayaEditorCore
42:     except:
43:         import MayaEditorCore
44: except ImportError:
45:     OpenMaya.MGlobal.displayError("Trouble importing MayaEditorCore Module")
46:     # throw exception and let maya deal with it
47:     raise
48: 
49: MayaEditorMixinWindow = None
50: 
51: 
52: def MayaEditorUIScript(restore=False):
53:     global MayaEditorMixinWindow
54:     import MayaEditorCore
55: 
56:     """ When the control is restoring, the workspace control has already been created and
57:         all that needs to be done is restoring its UI.
58:     """
59:     if restore == True:
60:         # Grab the created workspace control with the following.
61:         restoredControl = omui.MQtUtil.getCurrentParent()
62: 
63:     if MayaEditorMixinWindow is None:
64:         # Create a custom mixin widget for the first time
65:         print("creating a new ui")
66:         MayaEditorMixinWindow = MayaEditorCore.EditorDialog()
67:         MayaEditorMixinWindow.setObjectName("MayaEditor")
68: 
69:     if restore == True:
70:         # Add custom mixin widget to the workspace control
71:         mixinPtr = omui.MQtUtil.findControl(MayaEditorMixinWindow.objectName())
72:         omui.MQtUtil.addWidgetToMayaLayout(int(mixinPtr), int(restoredControl))
73:     else:
74:         # Create a workspace control for the mixin widget by passing all the needed parameters. See workspaceControl command documentation for all available flags.
75:         MayaEditorMixinWindow.show(dockable=True, height=600, width=800, uiScript="MayaEditorUIScript(restore=True)")
76: 
77:     return MayaEditorMixinWindow
78: 
79: 
80: """ Using the workspaceControl Maya command to query/edit flags about the created
81:     we can use maya.cmds.workspaceControl('MayaEditorWorkspaceControl', e=True,restore=True)
82:     to re-show the workspace editor if needed. Will need to add this to a button at some stage
83: """
84: 
85: 
86: maya_useNewAPI = True  # type: ignore
87: 
88: 
89: class MayaEditor(OpenMaya.MPxCommand):
90:     CMD_NAME = "MayaEditor"
91:     ui = None
92: 
93:     def __init__(self):
94:         super(MayaEditor, self).__init__()
95: 
96:     @classmethod
97:     def doIt(cls, args):
98:         """
99:         Called when the command is executed in script
100:         """
101:         ui = MayaEditorUIScript()
102:         if ui is not None:
103:             try:
104:                 cmds.workspaceControl("MayaEditorWorkspaceControl", e=True, restore=True)
105:             except:
106:                 pass
107:         return ui
108: 
109:     @classmethod
110:     def creator(cls):
111:         """
112:         Think of this as a factory
113:         """
114:         return MayaEditor()
115: 
116:     @classmethod
117:     def cleanup(cls):
118:         # cleanup the UI and call the destructors
119:         MayaEditor.ui.deleteLater()
120:         MayaEditor.ui = None
121: 
122: 
123: def initializePlugin(plugin):
124:     """
125:     Load our plugin
126:     """
127:     vendor = "NCCA"
128:     version = "1.0.0"
129: 
130:     plugin_fn = OpenMaya.MFnPlugin(plugin, vendor, version)
131:     try:
132:         plugin_fn.registerCommand(MayaEditor.CMD_NAME, MayaEditor.creator)
133:         # cmds.evalDeferred("cmds.MayaEditor()")
134:         mel.eval("""
135:                 global proc ScriptEditor() {
136:                     python("cmds.MayaEditor()");
137:                 }
138:             """)
139:     except:
140:         OpenMaya.MGlobal.displayError("Failed to register command: {0}".format(MayaEditor.CMD_NAME))
141: 
142: 
143: def uninitializePlugin(plugin):
144:     """
145:     Exit point for a plugin
146:     """
147:     # cleanup the dialog first
148:     MayaEditor.cleanup()
149:     mel.eval("""
150:             global proc ScriptEditor() {
151:                 if (`window -exists scriptEditorPanel1Window`) {
152:                     showWindow scriptEditorPanel1Window;
153:                 } else {
154:                     scriptedPanel -e -tearOff scriptEditorPanel1 scriptEditorPanel1Window;
155:                 }
156:             }
157:             """)
158:     plugin_fn = OpenMaya.MFnPlugin(plugin)
159:     try:
160:         plugin_fn.deregisterCommand(MayaEditor.CMD_NAME)
161:     except:
162:         OpenMaya.MGlobal.displayError("Failed to deregister command: {0}".format(MayaEditor.CMD_NAME))
163: 
164: 
165: if __name__ == "__main__":
166:     """
167:     So if we execute this in the script editor it will be a __main__ so we can put testing code etc here
168:     Loading the plugin will not run this
169:     As we are loading the plugin it needs to be in the plugin path.
170:     """
171: 
172:     plugin_name = "MayaEditor.py"
173: 
174:     cmds.evalDeferred('if cmds.pluginInfo("{0}", q=True, loaded=True): cmds.unloadPlugin("{0}")'.format(plugin_name))
175:     cmds.evalDeferred('if not cmds.pluginInfo("{0}", q=True, loaded=True): cmds.loadPlugin("{0}")'.format(plugin_name))

(End of file - total 175 lines)
</content>

Assistant (Build · Big Pickle · 10.1s)

Thinking:

The error is on line 31:

root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")

cmds.moduleInfo(path=True, moduleName="MayaEditor") returns None when the module "MayaEditor" is not found. This is the "No module path found" error.

The issue is that moduleInfo requires the Maya module to be properly set up (via a .mod file in the Maya modules path). The fallback should handle the case where the module isn't registered, and instead derive the path from the script's location.

Let me also check if there's a MayaEditor.mod file to understand how the module is set up.

Let me check how the path is intended to be resolved. The code on line 31 calls cmds.moduleInfo(path=True, moduleName="MayaEditor"). If the .mod file isn't properly installed, this returns None. Then line 37 tries sys.path.insert(0, root_path + "/plug-ins") which would fail because root_path is None.

The fix should handle the case where root_path is None and fall back to using the directory of the current script.

The problem is at line 31: cmds.moduleInfo(path=True, moduleName="MayaEditor") returns None if the Maya module isn't properly registered (no .mod file in the Maya modules path). Line 37 then tries None + "/plug-ins" which fails.

The fix: fall back to the script's own directory when moduleInfo returns None.

Tool: edit

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditor.py",
  "oldString": "# Grab the module root so we can append our python path\nroot_path = cmds.moduleInfo(path=True, moduleName=\"MayaEditor\")",
  "newString": "# Grab the module root so we can append our python path\nroot_path = cmds.moduleInfo(path=True, moduleName=\"MayaEditor\")\nif root_path is None:\n    root_path = os.path.dirname(os.path.abspath(__file__)).replace(\"/plug-ins\", \"\")"
}

Output:

Edit applied successfully.

Assistant (Build · Big Pickle · 3.3s)

Thinking:

Let me verify the fix looks correct.

Let me verify the result:

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditor.py",
  "offset": 29,
  "limit": 10
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditor.py</path>
<type>file</type>
<content>
29: 
30: # Grab the module root so we can append our python path
31: root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")
32: if root_path is None:
33:     root_path = os.path.dirname(os.path.abspath(__file__)).replace("/plug-ins", "")
34: try:
35:     import importlib
36: 
37:     # for debug purposed it is easier to re-load module
38:     # so append to path then try to delete if exists.

(Showing lines 29-38 of 177. Use offset=39 to continue.)
</content>

Assistant (Build · Big Pickle · 2.6s)

Fixed plug-ins/MayaEditor.py:31. When moduleInfo returns None (module not registered via .mod file), it now falls back to deriving the root path from the script's own location (__file__), stripping the /plug-ins suffix to get the repo root.


User

// Error: RuntimeError: file /Users/jmacey/teaching/Code/MayaEditor/plug-ins/MayaEditor.py line 31: No module path found // Warning: file: /Applications/Autodesk/maya2026/Maya.app/Contents/scripts/others/pluginWin.mel line 934: Failed to run file: /Users/jmacey/teaching/Code/MayaEditor/plug-ins/MayaEditor.py // Error: file: /Applications/Autodesk/maya2026/Maya.app/Contents/scripts/others/pluginWin.mel line 934: (MayaEditor)


Assistant (Build · Big Pickle · 9.3s)

Thinking:

The error is still happening on line 31, which means the fix I applied didn't take effect, or there's a different issue. But wait - the error says line 31, which is still the moduleInfo line. The issue must be that Maya is caching the old version of the plugin, or the user hasn't reloaded the plugin.

Actually, looking again at the error: RuntimeError: file /Users/jmacey/teaching/Code/MayaEditor/plug-ins/MayaEditor.py line 31: No module path found - this is a RuntimeError, not just a return value of None. Let me check if moduleInfo actually raises a RuntimeError when the module isn't found, rather than returning None.

Looking at the Maya documentation, cmds.moduleInfo(path=True, moduleName="MayaEditor") when the module doesn't exist, it raises a RuntimeError with "No module path found". So it's actually raising an exception, not returning None.

My fix checks for None but the function actually raises a RuntimeError. I need to catch the exception instead.

The error is a RuntimeError exception, not None return. moduleInfo raises when the module isn't found. The check for None never runs. Let me fix it to catch the exception.

Tool: edit

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditor.py",
  "oldString": "# Grab the module root so we can append our python path\nroot_path = cmds.moduleInfo(path=True, moduleName=\"MayaEditor\")\nif root_path is None:\n    root_path = os.path.dirname(os.path.abspath(__file__)).replace(\"/plug-ins\", \"\")",
  "newString": "# Grab the module root so we can append our python path\ntry:\n    root_path = cmds.moduleInfo(path=True, moduleName=\"MayaEditor\")\nexcept RuntimeError:\n    root_path = os.path.dirname(os.path.abspath(__file__)).replace(\"/plug-ins\", \"\")"
}

Output:

Edit applied successfully.

Assistant (Build · Big Pickle · 2.4s)

Fixed. cmds.moduleInfo() raises a RuntimeError when the module isn't registered — it doesn't return None. Changed the fallback to catch the exception instead.


User

Is there anything wrong with this mode file +MayaEditor 1.0 /Volumes/teaching/Code/MayaEditor MAYA_PLUG_IN_PATH +:= plug-ins MAYA_SCRIPT_PATH +:= plug-ins/AETemplates XBMLANGPATH +:= icons PYTHONPATH +:= plug-ins/MayaEditorCore icons: icons it doesn't seem to load


Assistant (Build · Big Pickle · 26.7s)

Thinking:

The user is asking about their Maya .mod file and why it doesn't seem to load. Let me look at this file and the module structure to understand the issue.

The mod file is at /Users/jmacey/Library/Preferences/Autodesk/maya/modules/MayaEditor.mod and contains:

+MayaEditor 1.0 /Volumes/teaching/Code/MayaEditor
MAYA_PLUG_IN_PATH +:= plug-ins
MAYA_SCRIPT_PATH +:= plug-ins/AETemplates
XBMLANGPATH +:= icons
PYTHONPATH +:= plug-ins/MayaEditorCore
icons: icons

Let me think about what's wrong here:

  1. The format of a Maya .mod file is:
[+]ModuleName version module_root_path
[environment variables]

The first line looks correct: +MayaEditor 1.0 /Volumes/teaching/Code/MayaEditor

  1. The environment variable lines use +:= which means "append to existing". This is correct syntax.

  2. The icons: icons line at the end - this is suspicious. In a .mod file, a line like icons: icons would be interpreted as setting an environment variable icons to icons, which doesn't make sense. Also, it might be confusing the parser.

Wait, actually looking at it more carefully, Maya .mod files support custom resource paths. The icons: line might be interpreted as a resource specification. But actually, the format for resources in .mod files is typically part of the first line or handled differently.

Actually, let me reconsider. The Maya module file format does support path:<resource> entries on the first line, like:

+ModuleName 1.0 module_root_path/icons:icons

But the line icons: icons on its own is not valid mod file syntax and could cause the parser to fail entirely.

Actually, I think the issue might be more subtle. Let me re-read the mod file documentation more carefully.

A Maya .mod file format:

  • Lines starting with # are comments
  • The first non-comment line defines the module: [+][platform:]module_name version_number module_root_path
  • Subsequent lines set environment variables using := or +:=
  • Blank lines are ignored

The line icons: icons is neither a valid module definition nor a valid environment variable assignment. The : without = or += would confuse the parser.

Actually wait - maybe icons: icons is being interpreted as an environment variable icons being set? In some Maya module formats, KEY: VALUE without = is valid for certain predefined keys? Let me think...

Actually, looking at it from Maya's perspective:

  • MAYA_PLUG_IN_PATH +:= plug-ins - valid, appends to PLUG_IN_PATH
  • MAYA_SCRIPT_PATH +:= plug-ins/AETemplates - valid
  • XBMLANGPATH +:= icons - valid
  • PYTHONPATH +:= plug-ins/MayaEditorCore - valid
  • icons: icons - INVALID - this line is not a valid mod file directive

The invalid icons: icons line would likely cause the module parser to fail and the entire module would not be loaded.

Let me look at the repo to see what's actually in the icons directory to understand what the intent was.

Let me check if this mod file is tracked in the repo or just user-generated.

The icons: icons line at the end is invalid Maya .mod syntax — it will cause the parser to fail and the entire module to not load. That line isn't a valid environment variable assignment (missing =/+=). The intent was likely to set XBMLANGPATH for icons, which is already done on the line above.

Let me check the repo for a reference mod file:

Tool: glob

Input:

{
  "pattern": "**/*.mod",
  "path": "/Volumes/teaching/Code/MayaEditor"
}

Output:

/Volumes/teaching/Code/MayaEditor/MayaEditor.mod

Tool: read

Input:

{
  "filePath": "/Users/jmacey/Library/Preferences/Autodesk/maya/modules/MayaEditor.mod"
}

Output:

<path>/Users/jmacey/Library/Preferences/Autodesk/maya/modules/MayaEditor.mod</path>
<type>file</type>
<content>
1: +MayaEditor 1.0 /Volumes/teaching/Code/MayaEditor
2: MAYA_PLUG_IN_PATH +:= plug-ins
3: MAYA_SCRIPT_PATH +:= plug-ins/AETemplates
4: XBMLANGPATH +:= icons
5: PYTHONPATH +:= plug-ins/MayaEditorCore
6: icons: icons

(End of file - total 6 lines)
</content>

Assistant (Build · Big Pickle · 2.9s)

Thinking:

The invalid icons: icons line is the problem. The repo's reference file might help confirm. Let me check.

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/MayaEditor.mod"
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/MayaEditor.mod</path>
<type>file</type>
<content>
1: + MayaEditor 1.0 /Users/jmacey/teaching/Code/MayaEditor
2: MAYA_PLUG_IN_PATH +:= plug-ins
3: 

(End of file - total 3 lines)
</content>

Assistant (Build · Big Pickle · 13.5s)

Thinking:

So the repo's mod file is clean - it doesn't have the icons: icons line. The user's installed version has that bogus line which is causing the module parser to fail.

The fix: remove the last line icons: icons from /Users/jmacey/Library/Preferences/Autodesk/maya/modules/MayaEditor.mod.

Actually, the repo's .mod file also has a space after +: + MayaEditor vs the user's +MayaEditor. Let me check if the space matters. Actually, looking at Maya docs, the format is [+][platform:]module_name so +MayaEditor is correct. A space after + would be + MayaEditor which might not work correctly. Hmm, actually in the Maya docs the format shows there can be a space. Let me not worry about that for now.

The main issue is clearly the icons: icons line. Let me fix the user's mod file.

Actually wait, should I modify the file? The user is asking me what's wrong. Let me tell them and then fix it.

Two issues:

  1. Line 6 icons: icons — Invalid syntax. Maya's .mod parser chokes on this line and rejects the entire module. XBMLANGPATH +:= icons on line 4 already handles the icon path.

  2. Repo's MayaEditor.mod at line 1 has + MayaEditor (space after +), but your installed copy has +MayaEditor (no space). The space is fine on the module definition line, but the icons: icons line is the killer.

Remove that last line and the module will load:

Tool: edit

Input:

{
  "filePath": "/Users/jmacey/Library/Preferences/Autodesk/maya/modules/MayaEditor.mod",
  "oldString": "icons: icons\n",
  "newString": ""
}

Output:

Edit applied successfully.

Assistant (Build · Big Pickle · 2.1s)

Removed the invalid icons: icons line from your installed .mod file. Reload Maya modules (restart Maya or use the module manager) and it should work.


User

cmds.MayaEditor() creating a new ui

Error: RuntimeError: AttributeError: file /Applications/Autodesk/maya2026/Maya.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/enum.py line 786: 0


Assistant (Build · Big Pickle · 10.9s)

Thinking:

The mod file is loading now (the plugin loaded successfully), and cmds.MayaEditor() runs and prints "creating a new ui", but then hits a RuntimeError with an AttributeError in the enum module at line 786, with value 0.

This is likely the same Qt.CTRL issue we thought we fixed earlier, but maybe there are more occurrences in the codebase. The error at enum.py line 786: 0 suggests something is being resolved to 0 (likely Qt.CTRL or similar deprecated enum value) inside the EditorDialog.py code.

Let me search for more Qt-related deprecation patterns that could cause this. The issue is that Qt.CTRL resolves to 0 in PySide6/Qt6, and when this 0 is passed to QKeySequence, it causes an enum error.

Wait, but I already fixed the two Qt.CTRL occurrences in EditorDialog.py. Let me check if there are more occurrences in other files. Also let me check if there's a PythonTextEdit.py or MelTextEdit.py that might have similar issues.

Let me also check the ConvertToPySide6.md file which seems to be a migration guide - maybe it has hints about what else needs to change.

Let me search more broadly for deprecated Qt patterns.

Let me search for more deprecated Qt patterns that could cause this:

Tool: grep

Input:

{
  "pattern": "Qt\\.(CTRL|SHIFT|ALT|META|Key_|ALTGR)",
  "path": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore"
}

Output:

Found 15 matches
/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:
  Line 223:         if event.key() == Qt.Key_Escape:

  Line 284:         show_output_window_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1))

  Line 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_0))


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/MelTextEdit.py:
  Line 92:                 event.key() == Qt.Key_Return

  Line 94:                 or event.key() == Qt.Key_F5

  Line 98:             elif event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier:


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/PythonTextEdit.py:
  Line 91:                 event.key() == Qt.Key_Return

  Line 93:                 or event.key() == Qt.Key_F5

  Line 97:             elif event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier:


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/TextEdit.py:
  Line 126:             if event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier:

  Line 130:                 event.key() in (Qt.Key_Plus, Qt.Key_Equal)

  Line 136:                 event.key() == Qt.Key_Minus and event.modifiers() == Qt.ControlModifier

  Line 140:             elif event.key() == Qt.Key_G and event.modifiers() == Qt.ControlModifier:

  Line 143:             elif event.key() == Qt.Key_F and event.modifiers() == Qt.ControlModifier:

  Line 147:             elif event.key() == Qt.Key_Return and not self.hasFocus():

Tool: grep

Input:

{
  "pattern": "Qt\\.(CTRL|SHIFT|ALT|META|Key_|ALTGR)",
  "path": "/Volumes/teaching/Code/MayaEditor/plug-ins",
  "include": "*.py"
}

Output:

Found 15 matches
/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:
  Line 223:         if event.key() == Qt.Key_Escape:

  Line 284:         show_output_window_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1))

  Line 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_0))


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/MelTextEdit.py:
  Line 92:                 event.key() == Qt.Key_Return

  Line 94:                 or event.key() == Qt.Key_F5

  Line 98:             elif event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier:


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/PythonTextEdit.py:
  Line 91:                 event.key() == Qt.Key_Return

  Line 93:                 or event.key() == Qt.Key_F5

  Line 97:             elif event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier:


/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/TextEdit.py:
  Line 126:             if event.key() == Qt.Key_S and event.modifiers() == Qt.ControlModifier:

  Line 130:                 event.key() in (Qt.Key_Plus, Qt.Key_Equal)

  Line 136:                 event.key() == Qt.Key_Minus and event.modifiers() == Qt.ControlModifier

  Line 140:             elif event.key() == Qt.Key_G and event.modifiers() == Qt.ControlModifier:

  Line 143:             elif event.key() == Qt.Key_F and event.modifiers() == Qt.ControlModifier:

  Line 147:             elif event.key() == Qt.Key_Return and not self.hasFocus():

Tool: grep

Input:

{
  "pattern": "\\bQt\\.[A-Za-z_]+ \\+",
  "path": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore",
  "include": "*.py"
}

Output:

No files found

Assistant (Build · Big Pickle · 7.7s)

Thinking:

OK, the grep for Qt.CTRL shows that the two occurrences in EditorDialog.py have already been fixed to Qt.KeyboardModifier.ControlModifier. The other files all use Qt.Key_* for key constants (which are fine in PySide6) and Qt.ControlModifier (not Qt.CTRL), which is the correct attribute name in both PySide2 and PySide6.

But wait - Qt.ControlModifier vs Qt.KeyboardModifier.ControlModifier. In PySide6, Qt.ControlModifier might still work as a shortcut, or it might be deprecated. Let me check...

Actually, the issue is something else entirely. The error is:

RuntimeError: AttributeError: file .../enum.py line 786: 0

This is happening after "creating a new ui" is printed, so the UI construction is failing somewhere inside EditorDialog.py. Let me look at the EditorDialog.__init__ to see what happens during construction.

Let me look at the EditorDialog.py file from the beginning.

The grep results show the fixed code is fine — no more Qt.CTRL usage. The Qt.Key_* and Qt.ControlModifier usages are correct PySide6 API. The error is happening during UI construction after "creating a new ui" is printed. Let me trace the error.

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "limit": 100
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py</path>
<type>file</type>
<content>
1: # Copyright (C) 2022  Jonathan Macey
2: #
3: # This program is free software: you can redistribute it and/or modify
4: # it under the terms of the GNU General Public License as published by
5: # the Free Software Foundation, either version 3 of the License, or
6: #  any later version.
7: #
8: #  This program is distributed in the hope that it will be useful,
9: #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10: #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11: #  GNU General Public License for more details.
12: #
13: #  You should have received a copy of the GNU General Public License
14: #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
15: """Editor Dialog Class for the NCCA Maya Editor.
16: 
17: This is the core Dialog class where all other elements are created and controlled. This can work stand alone as well as part of a plugin.
18: """
19: from pathlib import Path
20: from typing import Any
21: 
22: import maya.api.OpenMaya as OpenMaya
23: import maya.cmds as cmds
24: import maya.OpenMayaUI as omui
25: from maya.app.general.mayaMixin import MayaQWidgetDockableMixin
26: 
27: from PySide6.QtCore import *
28: from PySide6.QtGui import *
29: from PySide6.QtUiTools import *
30: from PySide6.QtWidgets import *
31: 
32: # Note this is from Maya not pyside so type hints not generated
33: from shiboken6 import wrapInstance  # type: ignore
34: 
35: from .EditorToolBar import EditorToolBar
36: from .MainUI import Ui_editor_dialog
37: from .MelTextEdit import MelTextEdit
38: from .OutputToolBar import OutputToolBar
39: from .PythonTextEdit import PythonTextEdit
40: from .SidebarModels import SideBarModels
41: from .TextEdit import TextEdit
42: from .Workspace import Workspace
43: 
44: 
45: def get_main_window() -> Any:
46:     """Return the maya main window for parenting.
47: 
48:     Grab the maya main window
49:     Returns : QWidget of the MayaMainWindow
50:     """
51:     window = omui.MQtUtil.mainWindow()
52:     return wrapInstance(int(window), QDialog)
53: 
54: 
55: 
56: 
57: class EditorDialogCore(QDialog):
58:     """Editor Dialog window main class we inherit from this for either a
59:     standalone editor or a mixin (dockable) maya one.
60: 
61:     Inherits from QDialog and loads the ui from files.
62:     """
63: 
64:     update_output = Signal(str)
65:     update_output_html = Signal(str)
66:     update_fonts = Signal(QFont)
67:     toggle_line_numbers = Signal(bool)
68:     editor_name = "NCCA_Script_Editor"
69: 
70:     def __init__(self, parent=None):
71:         """Construct the class.
72: 
73:         Parameters :
74:         parent (QWidget) : the Maya parent window Note this is set to None so we can use the get_main_window() function in maya
75:         If we run standalone we need to pass in a main window instance (see the EditorStandalone.py module)
76:         """
77:         super(EditorDialogCore, self).__init__(parent=parent)
78: 
79:         self.setObjectName(self.__class__.editor_name)
80:         # Register the callback to filter the outputs to out output window
81:         self.callback_id = OpenMaya.MCommandMessage.addCommandOutputCallback(
82:             self.message_callback, ""
83:         )
84:         # Load setting first as required by other elements
85:         self.settings = QSettings("NCCA", "NCCA_Maya_Editor")
86:         # Next the UI as again required for other things
87:         self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")
88:         self.python_icon = QIcon(":/icons/python.png")
89:         self.mel_icon = QIcon(":icons/mel.png")
90:         self.text_icon = QIcon(":icons/text.png")
91: 
92:         self.ui = Ui_editor_dialog()
93:         self.ui.setupUi(self)
94: 
95:         # This should make the window stay on top
96:         self.setWindowFlags(Qt.Tool)
97:         # as other things may depend on this create early
98:         self.create_output_window()
99:         self.create_tool_bar()
100:         self.create_menu_bar()

(Showing lines 1-100 of 687. Use offset=101 to continue.)
</content>

Assistant (Build · Big Pickle · 27.0s)

Thinking:

Line 87: self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor") — this is going to fail with the same RuntimeError "No module path found" that we fixed in the plugin! Only this is in the EditorDialogCore.__init__ in EditorDialog.py.

But wait, the error the user is reporting is:

# Error: RuntimeError: AttributeError: file .../enum.py line 786: 0

This is different from the "No module path found" error. It's an AttributeError about 0 in the enum module. So this line might have actually succeeded (since the mod file is now fixed).

Wait, the error might be coming from here after all. Let me check - if self.root_path raises a RuntimeError, the "creating a new ui" would print but then the init would fail with a RuntimeError during construction. But the error message says "RuntimeError: AttributeError" — that means a RuntimeError wrapping an AttributeError.

Hmm, actually cmds.moduleInfo raises a RuntimeError with message "No module path found". So if the mod file fix worked, this should be fine now. But the error they're showing is "RuntimeError: AttributeError: file .../enum.py line 786: 0".

Looking at the error more carefully:

RuntimeError: AttributeError: file /Applications/Autodesk/maya2026/Maya.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/enum.py line 786: 0

This is a Maya RuntimeError wrapping a Python AttributeError that occurs in Python's enum module at line 786. The "0" at the end is likely the value being passed to the enum constructor that doesn't match any valid enum member.

In PySide6 with Qt6, this typically happens when you try to create an enum from a raw integer that's 0 and doesn't match. The enum module's __call__ at line 786 validates the value.

Given that "creating a new ui" is printed (from line 65 of MayaEditor.py), the construction starts but then fails. The error at enum.py:786 with value 0 suggests something like:

  1. QKeySequence(Qt.CTRL + Qt.Key_1) — but I already fixed these. The user might not have reloaded the plugin.

Wait — the user needs to unload and reload the plugin for the Python code changes to take effect. The script says "creating a new ui" which means the MayaEditor.py code ran (so our mod fix worked and the import succeeded), but the EditorDialog.py code is the old cached version.

Actually, let me think about this differently. The MayaEditor.py imports MayaEditorCore from EditorDialog.py. If the user has been running the same Maya session without reloading the plugin, the Python code would be the cached version.

But they said "creating a new ui" which suggests the code is running. Let me look at line 87 again:

self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")

If the mod file fix worked, this should return the path. So this shouldn't be the issue.

The problem is likely elsewhere. Let me search for QKeySequence usage in the codebase to find more places where the + operator is used:

Actually, let me search for Qt.CTRL more broadly in ALL Python files.

Wait, the grep already showed no more Qt.CTRL in the source code. But maybe the issue is with something else entirely.

Let me look at the enum error more carefully. enum.py line 786 in Python 3.11 — what function is that?

In Python 3.11's enum.py, around line 786 is in the __call__ method of EnumMeta. The error says 0 — this means something is trying to create an enum with value 0.

In PySide6/Qt6, many enums are true Python enums. When you pass a raw integer to something expecting an enum, it could fail. Common suspects:

  1. Qt.Tool — this should still be fine
  2. QKeySequence(Qt.CTRL + Qt.Key_1) — we fixed these
  3. Something in the UI file parsing

Wait, I just realized — the user may not have reloaded the plugin! The changes we made to EditorDialog.py (fixing Qt.CTRL to Qt.KeyboardModifier.ControlModifier) would NOT take effect until the plugin is reloaded. Let me check — when the user runs cmds.MayaEditor(), the code imports MayaEditorCore. If MayaEditorCore was already imported in a previous session (or in this session), the cached module would be used.

Actually looking at the MayaEditor.py code:

try:
    print("deleting MayaEditorCore")
    del sys.modules["MayaEditorCore"]
    import MayaEditorCore
except:
    import MayaEditorCore

It tries to delete the stale module and re-import. So if the user reloads the plugin (unload + load), this would run and get the fresh code.

But wait — they might have just run cmds.MayaEditor() without reloading the plugin. In that case, the old cached EditorDialog.py would be used, with the Qt.CTRL + Qt.Key_1 code.

Actually, the try/except block in the plugin's main code only tries to delete/reload MayaEditorCore during module import. If the plugin is already loaded, MayaEditorUIScript() just calls import MayaEditorCore which would get the already-loaded module.

Hmm, but it says "creating a new ui" which means the plugin code IS running successfully. And if the module was already cached, the except path would import the cached version.

Let me look at the actual error trace again. The error enum.py:786: 0 is from the enum module when Python can't match value 0 to a valid enum variant.

In PySide6, this can happen with QKeySequence if you pass an invalid value. But we already fixed those.

Wait, let me look at other potential culprits. Let me search for QKeySequence in the codebase:

The user's error is during UI construction. Let me search for all QKeySequence and other potential enum 0 value issues:

Tool: grep

Input:

{
  "pattern": "QKeySequence",
  "path": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore"
}

Output:

Found 2 matches
/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:
  Line 284:         show_output_window_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1))

  Line 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_0))

Tool: grep

Input:

{
  "pattern": "setShortcut",
  "path": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore"
}

Output:

Found 2 matches
/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py:
  Line 284:         show_output_window_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1))

  Line 295:         show_sidebar_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_0))


Assistant (Build · Big Pickle · 39.6s)

Thinking:

OK, so the QKeySequence lines are already fixed. The issue is that the user probably hasn't reloaded the plugin yet since our fix. They need to unload and reload the plugin:

cmds.unloadPlugin("MayaEditor.py")
cmds.loadPlugin("MayaEditor.py")

But wait, even if they reload, the EditorDialog.py changes need to be picked up too. The import system tries to delete MayaEditorCore from sys.modules and reimport, so that should work.

Actually, let me re-think. The user ran cmds.MayaEditor() and got the error. The error says enum.py line 786: 0. This is a RuntimeError wrapping an AttributeError.

Wait, could the old .mod file (with the icons: icons line) have been loaded by Maya already, and perhaps Maya cached the module info? No, Maya modules are read at startup or when explicitly refreshed.

Let me think again about what could cause enum.py:786: 0.

In Python 3.11, enum.py line 786 is in the EnumMeta.__call__ method which looks up enum members by value. When value 0 is passed but there's no member with value 0, it raises an error.

Wait, actually I misread the error format. Let me read it carefully:

RuntimeError: AttributeError: file .../enum.py line 786: 0

In Maya, when Python raises an exception within a Maya API callback or command, it wraps it in a RuntimeError. The actual exception is AttributeError. And the 0 at the end is the argument/value.

Let me check what Qt.Tool does in PySide6. In the EditorDialog.py:

self.setWindowFlags(Qt.Tool)

Qt.Tool in PySide6 should be Qt.WindowType.Tool. In PySide6, Qt.Tool is still available as an alias, but it might have been deprecated. Let me check if Qt.Tool returns 0 in PySide6/Qt6.

Actually, I think the issue might be elsewhere. Let me look at line 88-90:

self.python_icon = QIcon(":/icons/python.png")
self.mel_icon = QIcon(":icons/mel.png")
self.text_icon = QIcon(":icons/text.png")

Wait, look at the icon paths. They use :/ for the first one and : for the others. The :/ prefix is for Qt resource system (qrc) while : is for Maya resource paths. But the first one uses :/icons/python.png which is a qrc path. The others use :icons/mel.png which is a Maya resource path.

Actually, QIcon expects a file path or a Qt resource path (:/...). The : prefix is Maya-specific and wouldn't work directly with QIcon. But this wouldn't cause an enum error.

Let me look at the MainUI.py file to see the setupUI.

Actually, the most likely cause of the enum.py:786: 0 error is that a key combination or modifier of value 0 is being passed somewhere.

Wait - I just realized something. In our fix, we changed:

QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1)

But Qt.KeyboardModifier.ControlModifier is a member of the KeyboardModifier enum, while Qt.Key_1 is from Qt.Key. These are different enum types! In Qt6, you can't directly OR two different enum types together. You'd need to convert one to an int first.

So the fix should be:

QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key.Key_1)

Wait, or more specifically, the keys need to be accessed from their proper enum classes. Let me think...

Actually, in PySide6, Qt.Key_1 as an attribute of the Qt module is an alias for Qt.Key.Key_1. Similarly, Qt.KeyboardModifier.ControlModifier is Qt.KeyboardModifier.ControlModifier.

The QKeySequence constructor expects either a string or a QKeyCombination. When you do modifier | key, it creates a QKeyCombination. But in Qt6 strict enum mode, you need to explicitly use the correct enum types.

Actually, let me check - in PySide6, Qt.Key_1 is an int (or enum), and Qt.KeyboardModifier.ControlModifier is also an int. When you OR them, you get an int. The QKeySequence(int) constructor should handle this.

But wait, the deprecation warning was about Qt.CTRL + Qt.Key_1. The + causing an int addition. The fix to use | is correct. And Qt.CTRL is deprecated and should be Qt.ControlModifier or Qt.KeyboardModifier.ControlModifier.

Hmm, let me look at this from a different angle. The error the user posted occurs right after "creating a new ui". Let me look at the EditorDialogCore __init__ to identify exactly where it might fail. The error is AttributeError: 0 in the enum module.

Let me check if Qt.Tool is valid in PySide6. Qt.Tool is Qt.WindowType.Tool. In PySide6, the Qt module attributes might have been reorganized.

Actually, I think the real issue might be something even simpler. Let me re-read the error:

RuntimeError: AttributeError: file .../enum.py line 786: 0

This is a RuntimeError wrapping an AttributeError. The "0" is the exception's argument. In Python 3.11's enum.py line 786:

def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None):

When you call an enum class with value 0, if 0 is not a valid member value, it raises an error.

In PySide6, this commonly happens with things like:

  • Qt.AlignLeft (should be Qt.AlignmentFlag.AlignLeft)
  • Various enum values that were plain ints in Qt5 but are now strict enums in Qt6

Let me check the UI setup and MainUI more carefully.

Actually, I bet the issue is in the MainUI.py or the .ui file loading. Let me check MainUI.py.

Wait, but the user said they opened the MayaEditor.mod file which suggests they're still debugging the module loading issue. Maybe the user hasn't reloaded the plugin yet with our changes. Let me just suggest they reload the plugin and try again.

Actually no, look at the output order:

cmds.MayaEditor()
creating a new ui
# Error: RuntimeError: AttributeError: file .../enum.py line 786: 0

"creating a new ui" is printed from line 65 of MayaEditor.py inside MayaEditorUIScript(). This means:

  1. The plugin is loaded and command runs
  2. MayaEditorUIScript() is called
  3. "creating a new ui" is printed
  4. MayaEditorCore.EditorDialog() constructor starts
  5. The constructor fails with the enum error

So the error is in EditorDialogCore.__init__(). Let me trace through it:

def __init__(self, parent=None):
    super(EditorDialogCore, self).__init__(parent=parent)
    self.setObjectName(self.__class__.editor_name)
    self.callback_id = OpenMaya.MCommandMessage.addCommandOutputCallback(...)
    self.settings = QSettings("NCCA", "NCCA_Maya_Editor")
    self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")  # might fail
    self.python_icon = QIcon(":/icons/python.png")
    self.mel_icon = QIcon(":icons/mel.png")
    self.text_icon = QIcon(":icons/text.png")
    self.ui = Ui_editor_dialog()
    self.ui.setupUi(self)
    self.setWindowFlags(Qt.Tool)
    self.create_output_window()
    self.create_tool_bar()
    self.create_menu_bar()

Wait, line 87: self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor") — this is the same call that was failing before in MayaEditor.py:31! If the mod file is fixed now, this should return a valid path. But if it's not fixed/cached, this would raise RuntimeError.

But the error message says "AttributeError: ... enum.py line 786: 0", not "RuntimeError: No module path found". So line 87 probably succeeds now.

Let me look at what could cause an enum error. The QIcon(":icons/mel.png") line — hmm, is the : prefix a valid QIcon prefix? In Qt, : is for Qt resources and :/ is also for Qt resources. Actually, :/ is the standard Qt resource prefix. : alone might not be valid. But this would cause a file-not-found issue, not an enum error.

Actually, in Maya, : is used for Maya resource paths (like :icons/mel.png). When you pass ":icons/mel.png" to QIcon, it might try to resolve it through Maya's path system. But I don't think this would cause an enum error.

Let me look at the setupUi method in MainUI.py.

Let me look at the MainUI.py to see what it does.

The old Qt.CTRL lines are already fixed. Let me look at what happens during UI construction — the error must be somewhere in EditorDialogCore.__init__. Let me trace it:

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "offset": 70,
  "limit": 120
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py</path>
<type>file</type>
<content>
70:     def __init__(self, parent=None):
71:         """Construct the class.
72: 
73:         Parameters :
74:         parent (QWidget) : the Maya parent window Note this is set to None so we can use the get_main_window() function in maya
75:         If we run standalone we need to pass in a main window instance (see the EditorStandalone.py module)
76:         """
77:         super(EditorDialogCore, self).__init__(parent=parent)
78: 
79:         self.setObjectName(self.__class__.editor_name)
80:         # Register the callback to filter the outputs to out output window
81:         self.callback_id = OpenMaya.MCommandMessage.addCommandOutputCallback(
82:             self.message_callback, ""
83:         )
84:         # Load setting first as required by other elements
85:         self.settings = QSettings("NCCA", "NCCA_Maya_Editor")
86:         # Next the UI as again required for other things
87:         self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")
88:         self.python_icon = QIcon(":/icons/python.png")
89:         self.mel_icon = QIcon(":icons/mel.png")
90:         self.text_icon = QIcon(":icons/text.png")
91: 
92:         self.ui = Ui_editor_dialog()
93:         self.ui.setupUi(self)
94: 
95:         # This should make the window stay on top
96:         self.setWindowFlags(Qt.Tool)
97:         # as other things may depend on this create early
98:         self.create_output_window()
99:         self.create_tool_bar()
100:         self.create_menu_bar()
101:         self.sidebar_models = SideBarModels(self)
102:         self.ui.sidebar_treeview.setModel(self.sidebar_models.active_model)
103:         self.ui.sidebar_selector.currentIndexChanged.connect(self.change_active_model)
104: 
105:         # connect tab close event
106:         self.ui.editor_tab.tabCloseRequested.connect(self.tab_close_requested)
107:         self.ui.editor_tab.currentChanged.connect(
108:             self.sidebar_models.code_model_needs_update
109:         )
110:         # setup  view sidebar
111:         self.ui.sidebar_treeview.setHeaderHidden(True)
112:         self.ui.sidebar_treeview.clicked.connect(self.sidebar_view_changed)
113:         # create workspace
114:         self.workspace = Workspace()
115:         # connect output window signals
116:         self.update_output.connect(self.output_window.append_plain_text)
117:         self.update_output_html.connect(self.output_window.append_html)
118:         # Finally load in settings and create live editors
119:         self.load_settings()
120:         self.create_live_editors()
121:         self.update_fonts.emit(self.font)
122:         self.show()
123: 
124:     def load_settings(self) -> None:
125:         """Load in the setting from QSettings for the editor."""
126:         splitter_settings = self.settings.value("splitter")
127:         self.ui.editor_splitter.restoreState(splitter_settings)  # type: ignore
128: 
129:         splitter_settings = self.settings.value("vertical_splitter")
130:         self.ui.vertical_splitter.restoreState(splitter_settings)  # type: ignore
131:         sz = self.settings.value(
132:             "size",
133:         )
134:         self.resize(self.settings.value("size", QSize(1024, 720)))
135:         workspace = self.settings.value("workspace")
136:         try:
137:             self.load_workspace_to_editor(workspace)
138:         except:
139:             pass
140:         self.settings.beginGroup("Font")
141:         name = self.settings.value("font-name", type=str)
142:         size = self.settings.value("font-size", type=int)
143:         weight = self.settings.value("font-weight", type=int)
144:         italic = self.settings.value("font-italic", type=bool)
145:         self.settings.endGroup()
146: 
147:         self.font = QFont(name, size, weight, italic)
148:         self.update_fonts.emit(self.font)
149: 
150:     def save_settings(self) -> None:
151:         self.settings.setValue("splitter", self.ui.editor_splitter.saveState())  # type: ignore
152:         self.settings.setValue("vertical_splitter", self.ui.vertical_splitter.saveState())  # type: ignore
153:         self.settings.setValue("size", self.size())
154:         self.settings.setValue("workspace", self.workspace.file_name)
155:         self.settings.beginGroup("Font")
156: 
157:         self.settings.setValue("font-name", self.font.family())
158:         self.settings.setValue("font-size", self.font.pointSize())
159:         self.settings.setValue("font-weight", self.font.weight())
160:         self.settings.setValue("font-italic", self.font.italic())
161:         self.settings.endGroup()
162: 
163:     def change_font(self) -> None:
164:         """Popup a change font dialog and set all editor fonts"""
165:         (ok, font) = QFontDialog.getFont(self)
166:         if ok:
167:             self.font = font
168:             self.update_fonts.emit(self.font)
169: 
170:     def debug(self, message: str) -> None:
171:         self.output_window.appendHtml(
172:             f'<b><p style="color:yellow">Debug :</p></b><p>{message}</p>'
173:         )
174: 
175:     def message_callback(self, message: str, mtype, client_data) -> None:
176:         """Use to put maya output to the output window.
177:         Parameters :
178:         message (str) : the message to print
179:         mtype (int) : type of message
180:         client_data : not used
181:         """
182:         colour = "white"
183:         message_prefix = ""
184:         if mtype == OpenMaya.MCommandMessage.kHistory:
185:             colour = "lightblue"
186:             message_prefix = "History : "
187:         elif mtype == OpenMaya.MCommandMessage.kDisplay:
188:             colour = "yellow"
189:         elif mtype == OpenMaya.MCommandMessage.kInfo:

(Showing lines 70-189 of 687. Use offset=190 to continue.)
</content>

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/MainUI.py"
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/MainUI.py</path>
<type>file</type>
<content>
1: # -*- coding: utf-8 -*-
2: 
3: ################################################################################
4: ## Form generated from reading UI file 'form.ui'
5: ##
6: ## Created by: Qt User Interface Compiler version 5.15.2
7: ##
8: ## WARNING! All changes made in this file will be lost when recompiling UI file!
9: ################################################################################
10: 
11: from PySide6.QtCore import *
12: from PySide6.QtGui import *
13: from PySide6.QtWidgets import *
14: 
15: 
16: class Ui_editor_dialog(object):
17:     def setupUi(self, editor_dialog):
18:         if not editor_dialog.objectName():
19:             editor_dialog.setObjectName("editor_dialog")
20:         editor_dialog.resize(995, 705)
21:         self.main_grid_layout = QGridLayout(editor_dialog)
22:         self.main_grid_layout.setObjectName("main_grid_layout")
23:         self.vertical_splitter = QSplitter(editor_dialog)
24:         self.vertical_splitter.setObjectName("vertical_splitter")
25:         self.vertical_splitter.setOrientation(Qt.Vertical)
26:         self.editor_splitter = QSplitter(self.vertical_splitter)
27:         self.editor_splitter.setObjectName("editor_splitter")
28:         sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
29:         sizePolicy.setHorizontalStretch(0)
30:         sizePolicy.setVerticalStretch(2)
31:         sizePolicy.setHeightForWidth(
32:             self.editor_splitter.sizePolicy().hasHeightForWidth()
33:         )
34:         self.editor_splitter.setSizePolicy(sizePolicy)
35:         self.editor_splitter.setOrientation(Qt.Horizontal)
36:         self.side_bar = QGroupBox(self.editor_splitter)
37:         self.side_bar.setObjectName("side_bar")
38:         sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
39:         sizePolicy1.setHorizontalStretch(0)
40:         sizePolicy1.setVerticalStretch(2)
41:         sizePolicy1.setHeightForWidth(self.side_bar.sizePolicy().hasHeightForWidth())
42:         self.side_bar.setSizePolicy(sizePolicy1)
43:         self.verticalLayout = QVBoxLayout(self.side_bar)
44:         self.verticalLayout.setObjectName("verticalLayout")
45:         self.sidebar_selector = QComboBox(self.side_bar)
46:         self.sidebar_selector.addItem("")
47:         self.sidebar_selector.addItem("")
48:         self.sidebar_selector.addItem("")
49:         self.sidebar_selector.setObjectName("sidebar_selector")
50: 
51:         self.verticalLayout.addWidget(self.sidebar_selector)
52: 
53:         self.sidebar_treeview = QTreeView(self.side_bar)
54:         self.sidebar_treeview.setObjectName("sidebar_treeview")
55:         sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
56:         sizePolicy2.setHorizontalStretch(0)
57:         sizePolicy2.setVerticalStretch(1)
58:         sizePolicy2.setHeightForWidth(
59:             self.sidebar_treeview.sizePolicy().hasHeightForWidth()
60:         )
61:         self.sidebar_treeview.setSizePolicy(sizePolicy2)
62: 
63:         self.verticalLayout.addWidget(self.sidebar_treeview)
64: 
65:         self.editor_splitter.addWidget(self.side_bar)
66:         self.editor_tab = QTabWidget(self.editor_splitter)
67:         self.editor_tab.setObjectName("editor_tab")
68:         sizePolicy3 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
69:         sizePolicy3.setHorizontalStretch(1)
70:         sizePolicy3.setVerticalStretch(1)
71:         sizePolicy3.setHeightForWidth(self.editor_tab.sizePolicy().hasHeightForWidth())
72:         self.editor_tab.setSizePolicy(sizePolicy3)
73:         self.editor_tab.setDocumentMode(True)
74:         self.editor_tab.setTabsClosable(True)
75:         self.editor_tab.setMovable(True)
76:         self.editor_splitter.addWidget(self.editor_tab)
77:         self.vertical_splitter.addWidget(self.editor_splitter)
78:         self.output_window_group_box = QGroupBox(self.vertical_splitter)
79:         self.output_window_group_box.setObjectName("output_window_group_box")
80:         self.output_window_layout = QVBoxLayout(self.output_window_group_box)
81:         self.output_window_layout.setObjectName("output_window_layout")
82:         self.output_dock = QDockWidget(self.output_window_group_box)
83:         self.output_dock.setObjectName("output_dock")
84:         self.dockWidgetContents_2 = QWidget()
85:         self.dockWidgetContents_2.setObjectName("dockWidgetContents_2")
86:         self.output_dock.setWidget(self.dockWidgetContents_2)
87: 
88:         self.output_window_layout.addWidget(self.output_dock)
89: 
90:         self.vertical_splitter.addWidget(self.output_window_group_box)
91: 
92:         self.main_grid_layout.addWidget(self.vertical_splitter, 1, 0, 1, 1)
93: 
94:         self.dock_widget = QDockWidget(editor_dialog)
95:         self.dock_widget.setObjectName("dock_widget")
96:         self.dock_widget.setFloating(False)
97:         self.dock_widget.setFeatures(
98:             QDockWidget.DockWidgetFloatable | QDockWidget.DockWidgetMovable
99:         )
100:         self.dock_widget.setAllowedAreas(Qt.AllDockWidgetAreas)
101:         self.dockWidgetContents = QWidget()
102:         self.dockWidgetContents.setObjectName("dockWidgetContents")
103:         self.dock_widget.setWidget(self.dockWidgetContents)
104: 
105:         self.main_grid_layout.addWidget(self.dock_widget, 0, 0, 1, 1)
106: 
107:         self.retranslateUi(editor_dialog)
108: 
109:         self.editor_tab.setCurrentIndex(-1)
110: 
111:         QMetaObject.connectSlotsByName(editor_dialog)
112: 
113:     # setupUi
114: 
115:     def retranslateUi(self, editor_dialog):
116:         editor_dialog.setWindowTitle(
117:             QCoreApplication.translate("editor_dialog", "NCCA Editor", None)
118:         )
119:         self.side_bar.setTitle(
120:             QCoreApplication.translate("editor_dialog", "Files", None)
121:         )
122:         self.sidebar_selector.setItemText(
123:             0, QCoreApplication.translate("editor_dialog", "Workspace", None)
124:         )
125:         self.sidebar_selector.setItemText(
126:             1, QCoreApplication.translate("editor_dialog", "File System", None)
127:         )
128:         self.sidebar_selector.setItemText(
129:             2, QCoreApplication.translate("editor_dialog", "Code Outline", None)
130:         )
131: 
132:         self.output_window_group_box.setTitle(
133:             QCoreApplication.translate("editor_dialog", "Output Window", None)
134:         )
135:         self.dock_widget.setWindowTitle(
136:             QCoreApplication.translate("editor_dialog", "Script Controls", None)
137:         )
138: 
139:     # retranslateUi

(End of file - total 139 lines)
</content>

Assistant (Build · Big Pickle · 13.8s)

Thinking:

OK, so the MainUI.py looks fine — it's generated from a .ui file and uses standard PySide6 APIs.

Now, looking at EditorDialog.py line 87:

self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")

This WILL fail with the same RuntimeError if the mod file isn't properly loaded. But the user fixed the mod file (removed icons: icons), so maybe it's working now?

Wait, the user's error message is new - it's about enum.py:786: 0. Let me think about what could cause this.

Looking at line 96:

self.setWindowFlags(Qt.Tool)

In PySide6, Qt.Tool is an alias for Qt.WindowType.Tool. This should still work. Let me verify.

Actually, looking at the full __init__ again, I need to think about what happens step by step. The error "RuntimeError: AttributeError: ... enum.py line 786: 0" happens during EditorDialogCore.__init__().

In Python 3.11, enum.py:786 is in the __call__ of EnumMeta. It's trying to find a member by value and the value is 0. This typically happens when you do something like Qt.AlignmentFlag(0) or similar.

Wait — let me look at self.setWindowFlags(Qt.Tool). setWindowFlags takes Qt.WindowFlags. In PySide6, Qt.WindowFlags is a flag type. Qt.Tool is Qt.WindowType.Tool.

Hmm, actually Qt.Tool might have been deprecated. In PySide6, it should be Qt.WindowType.Tool. But Qt.Tool should still work as an attribute access.

Actually, let me look at this from a completely different angle. Maybe the issue isn't in EditorDialog.py at all, but in one of the imported modules (like TextEdit, PythonTextEdit, etc.) during their constructor calls, or during create_output_window(), create_tool_bar(), or create_menu_bar().

Let me look at those methods.

Tool: read

Input:

{
  "offset": 200,
  "filePath": "/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py",
  "limit": 100
}

Output:

<path>/Volumes/teaching/Code/MayaEditor/plug-ins/MayaEditorCore/EditorDialog.py</path>
<type>file</type>
<content>
200:         elif mtype == OpenMaya.MCommandMessage.kResult:
201:             colour = "lightblue"
202:             message_prefix = "Result :"
203: 
204:         # this moves to the end so we don't get double new lines etc
205:         # self.output_window.moveCursor(QTextCursor.End)
206:         html = f'<p style="color:{colour}"><pre>{message_prefix}{message}</pre></p>'
207:         self.update_output_html.emit(html)
208: 
209:     def closeEvent(self, event: QCloseEvent) -> None:
210:         """Event called when the Dialog closeEvent is  triggered.
211: 
212:         We ensure that the setting are saved to the default settings.
213:         Parameters :
214:         event (QCloseEvent) : event passed in to close
215:         """
216:         OpenMaya.MMessage.removeCallback(self.callback_id)
217:         self.save_settings()
218:         self.workspace.close()
219:         super(EditorDialog, self).closeEvent(event)
220: 
221:     def keyPressEvent(self, event: QKeyEvent) -> None:
222: 
223:         if event.key() == Qt.Key_Escape:
224:             return True
225:         else:
226:             return super().keyPressEvent(event)
227: 
228:     def create_menu_bar(self) -> None:
229:         """Create the menubar for the editor."""
230:         self.menu_bar = QMenuBar()
231:         file_menu = QMenu("&File")
232:         self.menu_bar.addMenu(file_menu)
233:         open_action = QAction("&Open", self)
234:         open_action.triggered.connect(self.open_file)  # type: ignore
235:         file_menu.addAction(open_action)
236: 
237:         new_action = QAction("&New", self)
238:         new_action.triggered.connect(self.new_file)  # type: ignore
239:         file_menu.addAction(new_action)
240: 
241:         workspace_menu = QMenu("&Workspace")
242:         new_workspace = QAction("New Workspace", self)
243:         new_workspace.triggered.connect(self.new_workspace)  # type: ignore
244:         workspace_menu.addAction(new_workspace)
245:         # open workspace
246:         open_workspace = QAction("Open Workspace", self)
247:         open_workspace.triggered.connect(self.open_workspace)  # type: ignore
248:         workspace_menu.addAction(open_workspace)
249:         # save workspace
250:         save_workspace = QAction("Save Workspace", self)
251:         save_workspace.triggered.connect(self.save_workspace)  # type: ignore
252:         workspace_menu.addAction(save_workspace)
253:         # close workspace
254:         close_workspace = QAction("Close Workspace", self)
255:         close_workspace.triggered.connect(self.close_workspace)  # type: ignore
256:         workspace_menu.addAction(close_workspace)
257: 
258:         self.menu_bar.addMenu(workspace_menu)
259: 
260:         settings_menu = QMenu("&Settings")
261:         # Add settings Menu
262: 
263:         # add font action
264:         # font_menu = QMenu("&Font", self)
265:         change_font_action = QAction("Change Font", self)
266:         settings_menu.addAction(change_font_action)
267:         change_font_action.triggered.connect(self.change_font)
268: 
269:         # show line numbers
270:         show_line_numbers_action = QAction("Show Line Numbers", self)
271:         settings_menu.addAction(show_line_numbers_action)
272:         show_line_numbers_action.toggled.connect(self.show_line_numbers)
273:         show_line_numbers_action.setCheckable(True)
274:         show_line_numbers_action.setChecked(True)
275: 
276:         # show output window
277:         show_output_window_action = QAction("Show Output Window", self)
278:         settings_menu.addAction(show_output_window_action)
279:         show_output_window_action.toggled.connect(
280:             lambda state: self.ui.output_window_group_box.setVisible(state)
281:         )
282:         show_output_window_action.setCheckable(True)
283:         show_output_window_action.setChecked(True)
284:         show_output_window_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_1))
285: 
286:         # show sidebar window
287:         show_sidebar_action = QAction("Show Sidebar", self)
288:         settings_menu.addAction(show_sidebar_action)
289:         show_sidebar_action.toggled.connect(
290:             lambda state: self.ui.side_bar.setVisible(state)
291:         )
292:         show_sidebar_action.setCheckable(True)
293:         show_sidebar_action.setChecked(True)
294: 
295:         show_sidebar_action.setShortcut(QKeySequence(Qt.KeyboardModifier.ControlModifier | Qt.Key_0))
296: 
297:         # Menu to main menu bar
298:         self.menu_bar.addMenu(settings_menu)
299: 

(Showing lines 200-299 of 687. Use offset=300 to continue.)
</content>

Assistant (Build · Big Pickle · 63.7s)

Thinking:

Looking at line 223:

if event.key() == Qt.Key_Escape:
    return True

event.key() returns a Qt.Key enum. Qt.Key_Escape in PySide6 should be Qt.Key.Key_Escape. This should work as a comparison.

Wait — could the issue be that Qt.Key_Escape resolves to 0 in PySide6? Let me think... No, Qt.Key_Escape would be some non-zero value like 0x01000000 or similar. The 0 in the error message suggests something is truly 0.

Let me think about what could be 0:

  • Qt.AlignLeft might have value 0 (in Qt6, the alignment flags changed)
  • But I don't see any alignment usage in the constructor

Actually, let me reconsider. Maybe the error isn't even from our code. Maybe cmds.moduleInfo(path=True, moduleName="MayaEditor") is what's failing. The mod file fix might need Maya to be restarted for it to take effect. Maya reads .mod files at startup. If the user didn't restart Maya, the module is still not registered.

But the error message is different now: AttributeError: ... enum.py line 786: 0 instead of RuntimeError: No module path found. So something different is happening.

Actually, wait. Let me re-read the original error from the very first message:

# Error: RuntimeError: AttributeError: file /Applications/Autodesk/maya2026/Maya.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/enum.py line 786: 0

This error was in the FIRST message, before we made any changes. Looking at the full error history:

  1. First error: The Qt.CTRL + Qt.Key_X deprecation warnings + RuntimeError: AttributeError: ... enum.py line 786: 0
  2. Second error: RuntimeError: No module path found (after mod file but before our fix)
  3. Third error: still RuntimeError: No module path found (my None check fix didn't work)
  4. Fourth error: same error still showing at line 31 (user still hasn't reloaded)
  5. Now: RuntimeError: AttributeError: ... enum.py line 786: 0 again

Wait, I need to re-read the conversation more carefully:

Message 1: User shows the + deprecation warnings + RuntimeError: AttributeError: ... enum.py line 786: 0

  • I fixed Qt.CTRL + Qt.Key_XQt.KeyboardModifier.ControlModifier | Qt.Key_X

Message 2: User shows RuntimeError: No module path found at line 31

  • I added fallback for None return

Message 3: Same error still at line 31

  • I changed to try/except

Message 4: User shows the .mod file content and asks about it

  • I fixed the .mod file (removed icons: icons)

Message 5: User runs cmds.MayaEditor() and gets:

creating a new ui
RuntimeError: AttributeError: ... enum.py line 786: 0

So NOW the mod file is fixed (the plugin loads!), and the MayaEditorUIScript runs (prints "creating a new ui"), but the construction of EditorDialogCore fails.

The question is: why? We fixed the Qt.CTRL lines. But the user may not have reloaded the plugin yet. They just fixed the .mod file — but Maya modules are read at startup. The mod file change would only take effect after restarting Maya.

Wait no — actually Maya does re-read module files in some cases. And the fact that "creating a new ui" prints means the plugin's import succeeded (import MayaEditorCore worked), which means the mod file is being recognized.

Actually, hold on. Let me re-read MayaEditor.py line 31:

root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")

I changed this to a try/except. But in EditorDialog.py line 87:

self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")

This is STILL the old code without try/except. If the mod file IS properly loaded now, this line should work fine. But if it's NOT loaded, this line would fail with "No module path found" RuntimeError, not the enum error.

So either:

  1. The mod file is properly loaded (moduleInfo succeeds), and something else causes the enum error
  2. OR the mod file isn't loaded, but the moduleInfo error is caught somewhere

Actually, I wonder if Maya2026 changed moduleInfo to return None instead of raising when not found. In that case, self.root_path would be None, and subsequent code would use it for path construction, which might fail differently.

But the error is enum.py:786: 0, not a path-related error.

Let me think about what's different now. The user says they fixed the .mod file. Then they ran cmds.MayaEditor().

If the plugin was already loaded and they didn't reload:

  • MayaEditorUIScript would use the cached MayaEditorCore module
  • The cached module still has the old Qt.CTRL + Qt.Key_1 code
  • And those would cause the enum error because Qt.CTRL might be 0 in PySide6

Wait! I think I understand now. Qt.CTRL in PySide6/Qt6 might be 0 or not a valid enum value. When you do Qt.CTRL + Qt.Key_1, the + operator is the issue. But in the NEW code, we changed to Qt.KeyboardModifier.ControlModifier | Qt.Key_1.

But what if the user didn't reload the plugin? Then the old code with Qt.CTRL + Qt.Key_1 is still running. The deprecation warning told them Qt.CTRL + Qt.Key_1 is deprecated, but it still worked (with warnings). Then the enum error at enum.py:786: 0 suggests that in Maya2026's version of PySide6, Qt.CTRL literally has value 0.

In PySide6 (especially newer versions), the enum values might have been restructured so that Qt.CTRL is no longer a valid attribute or has value 0.

Actually wait, Qt.CTRL might still exist but return Qt.KeyboardModifier.ControlModifier which has integer value 0 in some Qt6 builds? No, ControlModifier should have value 0x04000000 or similar.

Hmm, I think the real issue is that maybe Qt.KeyboardModifier.ControlModifier is the wrong fix. Maybe the real issue is something else entirely.

Actually, let me look at this from the Python 3.11 enum.py perspective. At line 786:

In Python 3.11.0's enum.py:

def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None):
    if names is None:  # simple value lookup
        return cls.__new__(cls, value)

The __new__ method at some point validates the value. If the value is 0 and it doesn't match any member, it raises an error.

Now, in PySide6 with Qt6 enums being "scoped" or "strict", you can't pass raw integers. The QKeySequence(int) constructor might internally call the enum constructor with the int value, and if the combination doesn't match a valid key combination, it might fail.

But we already know QKeySequence(ControlModifier | Key_1) should be valid.

Let me go back to basics. What if the issue is NOT in create_menu_bar at all? What if it's earlier?

Looking at __init__:

super(EditorDialogCore, self).__init__(parent=parent)
self.setObjectName(self.__class__.editor_name)
self.callback_id = OpenMaya.MCommandMessage.addCommandOutputCallback(...)
self.settings = QSettings("NCCA", "NCCA_Maya_Editor")
self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor")
self.python_icon = QIcon(":/icons/python.png")
self.mel_icon = QIcon(":icons/mel.png")
self.text_icon = QIcon(":icons/text.png")
self.ui = Ui_editor_dialog()
self.ui.setupUi(self)
self.setWindowFlags(Qt.Tool)
self.create_output_window()
self.create_tool_bar()
self.create_menu_bar()

Wait — could cmds.moduleInfo be raising a RuntimeError with the message "No module path found", and that RuntimeError somehow manifests as an AttributeError in the enum module? That doesn't make sense.

Unless... cmds.moduleInfo returns None and then None + "/plug-ins" fails somewhere... but wait, self.root_path is never used with string operations in __init__. Let me search for where it's used.

Actually, let me just look at what the actual error is. The user said:

# Error: RuntimeError: AttributeError: file .../enum.py line 786: 0

The "0" is the attribute name or value being looked up. In Python 3.11's enum.py line 786, the code is something like:

result = cls._member_map_.get(value)

and if not found, it raises ValueError or TypeError with the value.

Wait, actually I need to look at Maya Python 3.11's enum.py. The error says "AttributeError", not "ValueError" or "TypeError". So an AttributeError is being raised within enum.py.

Looking at Python 3.11.0 enum.py around line 786 (in the EnumMeta.__new__ method), there's code that checks the value. An AttributeError with value "0" would mean something like accessing an attribute named "0" on a class.

Hmm, this could happen if Qt.KeyboardModifier.ControlModifier is actually an IntFlag and when you do Qt.KeyboardModifier.ControlModifier | Qt.Key_1, the result is an int, and then QKeySequence(int) tries to look it up in an enum map and fails.

OK, I think I'm overthinking this. Let me take a step back.

The user had two issues:

  1. Module loading (mod file fix)
  2. Qt enum compatibility (our code changes)

The mod file should be fixed now. The question is whether the Python code changes took effect.

The user needs to reload the plugin for the Python changes to take effect. Maya caches Python modules in sys.modules. Even if the .py file on disk has changed, the running Python process still uses the old cached module.

The MayaEditor.py has a mechanism to delete and re-import MayaEditorCore, but this only runs when the plugin is (re)loaded. If the user just ran cmds.MayaEditor() without unloading/reloading the plugin first, the old cached code is used.

Let me suggest the user reload the plugin.

But wait — maybe the user DID reload the plugin (the moduleInfo now works, which it didn't before). If they reloaded, the new code should be picked up.

Actually, looking at the flow again:

  1. cmds.unloadPlugin("MayaEditor.py") — this removes the plugin
  2. cmds.loadPlugin("MayaEditor.py") — this runs MayaEditor.py again from scratch

When MayaEditor.py runs:

sys.path.insert(0, root_path + "/plug-ins")

This inserts the plug-ins path. Then:

try:
    del sys.modules["MayaEditorCore"]
    import MayaEditorCore
except:
    import MayaEditorCore

This would import the (possibly fixed) MayaEditorCore package from disk.

If the user reloaded, our fixes should take effect. If the user just ran cmds.MayaEditor() again, the old cached code runs.

The error the user is seeing is the same enum error from the original message. So either:

  1. The user didn't reload the plugin (old code still cached)
  2. Or there's another enum issue we haven't found

Let me look more carefully at the error. The error says RuntimeError: AttributeError. In Maya, when a Python callback raises an exception, Maya wraps it in a RuntimeError. So the actual Python exception is AttributeError from enum.py:786 with the message 0.

An AttributeError in an enum class would happen if someone tries to access an attribute that doesn't exist on the enum class. For example:

class MyEnum(Enum):
    A = 1
    B = 2

MyEnum(3)  # ValueError, not AttributeError

Wait, AttributeError... could it be something like Qt.CTRL where CTRL is not an attribute of Qt? In that case, it would raise AttributeError: module 'PySide6.QtCore.Qt' has no attribute 'CTRL'. But the error says "enum.py line 786: 0" which points to inside the enum module.

Hmm, let me look at this from yet another angle. Maybe the issue is that our fix with Qt.KeyboardModifier.ControlModifier | Qt.Key_1 doesn't work in PySide6 either, because | between different enum types doesn't work properly in Qt6's strict enum system, and it produces 0.

Let me verify: in PySide6:

  • Qt.KeyboardModifier.ControlModifier has a value of 0x04000000
  • Qt.Key_1 has a value of 0x31 (49 in ASCII)

0x04000000 | 0x31 = 0x04000031, which is definitely not 0.

But wait — what if the | operator between Qt.KeyboardModifier.ControlModifier (a KeyboardModifier enum) and Qt.Key_1 (a Key enum) returns 0 because these types don't support cross-type bitwise OR?

In PySide6, Qt.KeyboardModifier is a Flag type and Qt.Key is an Enum type. The | operator between a Flag and an Enum might not work as expected.

Actually, PySide6 defines __or__ on these enum types to return int, so Qt.KeyboardModifier.ControlModifier | Qt.Key_1 should return an int like 0x04000031. Then QKeySequence(0x04000031) should work.

OK I think the real problem is that the user hasn't reloaded the plugin. Let me just ask them to try unloading and reloading the plugin.

Actually, wait. Let me re-read the user's message:

cmds.MayaEditor()
creating a new ui
# Error: RuntimeError: AttributeError: file .../enum.py line 786: 0

If the user simply ran cmds.MayaEditor() in the existing Maya session (after fixing the mod file), the plugin command would execute but the Python module MayaEditorCore would be the cached old version. Our source code changes wouldn't take effect.

The fix: tell the user to reload the plugin, or use the hot-reload script.

But actually, there's also the possibility that the error is NOT from the Qt.CTRL line at all, but from something else entirely. Let me look at line 96:

self.setWindowFlags(Qt.Tool)

In PySide6, Qt.Tool might have changed. Let me check... Actually, Qt.Tool exists as an attribute in PySide6. It's Qt.WindowType.Tool. The attribute Qt.Tool should still be accessible.

Hold on, the user opened drag_to_maya.py and the .mod file in the system reminder. These may suggest they're trying to set things up. The error might actually be from the old code.

Let me just tell the user to reload the plugin and try again. If the error persists, we'll debug further.

Actually, I just realized: the EditorDialog.py code has self.root_path = cmds.moduleInfo(path=True, moduleName="MayaEditor") on line 87. Even if the mod file is fixed, Maya might need a restart for the module info to be available. Maya reads .mod files in the modules directory at startup. It doesn't re-read them on the fly (though there are ways to refresh).

But the user got "creating a new ui" which means line 65 of MayaEditor.py ran successfully, which means the import of MayaEditorCore worked. So either:

  1. The mod file took effect (Maya re-read it somehow)
  2. Or the module was already imported from a previous session and is cached

Actually, if the module was already loaded and cached, line 31 of MayaEditor.py (root_path = cmds.moduleInfo(...)) would still fail (as it did in the previous errors). But now the error is different — it's the enum error, which suggests the module loaded successfully. This means the mod file fix worked!

But the enum error persists because our Qt.CTRL fix hasn't been applied yet (the Python modules are cached in memory).

Let me tell the user to reload the plugin.

Wait, I should also check: is there something else in EditorDialog.py that could cause enum.py:786: 0? Let me search for all potential enum issues.

Let me look at setWindowFlags(Qt.Tool). In PySide6, does Qt.Tool actually work?

Let me check what Qt.Tool resolves to in PySide6. If it's 0, that would be the issue. But that seems unlikely.

Actually, I just realized I can check this by looking at what the Qt namespace provides. In PySide6, Qt.Tool should be Qt.WindowType.Tool, which is a non-zero value.

Let me just tell the user to reload the plugin.

The .mod file fix worked — "creating a new ui" printed, meaning the plugin loaded and MayaEditorCore was imported. But the Python source changes (the Qt.CTRLQt.KeyboardModifier.ControlModifier fix) won't take effect until the plugin is reloaded. Maya caches Python modules in memory.

Reload the plugin:

cmds.unloadPlugin("MayaEditor.py")
cmds.loadPlugin("MayaEditor.py")
cmds.MayaEditor()

If you've already been doing that and the error persists, there's another enum issue elsewhere. Let me know and I'll dig deeper.


User

ls


Assistant (Build · Big Pickle · 3.4s)

Thinking:

The user just typed "ls" - they want to list the files in the current directory.

Tool: read

Input:

{
  "filePath": "/Volumes/teaching/Code/MayaEditor"
}

Output:

<path>/Volumes/teaching/Code/MayaEditor</path>
<type>directory</type>
<entries>
__pycache__/
.DS_Store
.git/
.gitignore
.idea/
.mypy_cache/
.qt_for_python/
.ropeproject/
AGENTS.md
ConvertToPySide6.md
DebugEditor.py
drag_to_maya.py
EditorStandalone.py
eval.ma
images/
installEditor.py
LICENCE.md
MayaEditor.mod
MayaEditor.pyproject
MayaEditor.pyproject.user
MayaEditor.workspace
MayaEditor.workspace1
mypy.ini
plug-ins/
README.md
SimpleWorkspace.workspace
test_ast.py
test_mayaPySide.py
test.txt
TestJedi.py
TestWorkspace.workspace
tmp.txt
TODO.md
untitled.ma

(34 entries)
</entries>

Assistant (Build · Big Pickle · 2.8s)

Thinking:

The user ran "ls" to see the files in the current directory. I've shown them the directory listing. That's all they asked for.

/Volumes/teaching/Code/MayaEditor — 34 entries as shown above.