|
| 1 | +Tested https://github.gg |
| 2 | + |
| 3 | +Full link: |
| 4 | +https://github.gg/plasmax/PythonEditor/?prompt=I%27d+like+to+add+a+feature+where+you+can+set+a+knob+as+context+to+run+the+code+in%2C+using+%22nuke.runIn%28%29%22.+the+context+would+display+below+the+tab+and+use+knob.fullyQualifiedName%28%29%2C+and+I+should+be+able+to+edit+the+context+easily.&max_size=57 |
| 5 | + |
| 6 | +Prompt: |
| 7 | +I'd like to add a feature where you can set a knob as context to run the code in, using "nuke.runIn()". the context would display below the tab and use knob.fullyQualifiedName(), and I should be able to edit the context easily. |
| 8 | +-- |
| 9 | + |
| 10 | +Token Usage: |
| 11 | +GitHub Tokens: 190454 |
| 12 | +LLM Input Tokens: 190508 |
| 13 | +LLM Output Tokens: 2270 |
| 14 | +Total Tokens: 383232 |
| 15 | + |
| 16 | +FileTree: |
| 17 | +.github/workflows/python-app.yml |
| 18 | +.gitignore |
| 19 | +PythonEditor/__init__.py |
| 20 | +PythonEditor/_version.py |
| 21 | +PythonEditor/app/__init__.py |
| 22 | +PythonEditor/app/nukefeatures/__init__.py |
| 23 | +PythonEditor/app/nukefeatures/editorknob.py |
| 24 | +PythonEditor/app/nukefeatures/jupyter_nodebook.py |
| 25 | +PythonEditor/app/nukefeatures/nodepanels.py |
| 26 | +PythonEditor/app/nukefeatures/nukedock.py |
| 27 | +PythonEditor/app/nukefeatures/nukeinit.py |
| 28 | +PythonEditor/core/__init__.py |
| 29 | +PythonEditor/core/execute.py |
| 30 | +PythonEditor/core/streams.py |
| 31 | +PythonEditor/models/__init__.py |
| 32 | +PythonEditor/models/xmlmodel.py |
| 33 | +PythonEditor/six.py |
| 34 | +PythonEditor/ui/Qt.py |
| 35 | +PythonEditor/ui/__init__.py |
| 36 | +PythonEditor/ui/dialogs/__init__.py |
| 37 | +PythonEditor/ui/dialogs/objectinspector.py |
| 38 | +PythonEditor/ui/dialogs/popupline.py |
| 39 | +PythonEditor/ui/dialogs/popups.py |
| 40 | +PythonEditor/ui/dialogs/preferences.py |
| 41 | +PythonEditor/ui/dialogs/shortcuteditor.py |
| 42 | +PythonEditor/ui/editor.py |
| 43 | +PythonEditor/ui/features/__init__.py |
| 44 | +PythonEditor/ui/features/action_register.json |
| 45 | +PythonEditor/ui/features/actions.py |
| 46 | +PythonEditor/ui/features/autocompletion.py |
| 47 | +PythonEditor/ui/features/autosavexml.py |
| 48 | +PythonEditor/ui/features/contextmenu.py |
| 49 | +PythonEditor/ui/features/linenumberarea.py |
| 50 | +PythonEditor/ui/features/nukepalette.py |
| 51 | +PythonEditor/ui/features/search.py |
| 52 | +PythonEditor/ui/features/shortcuts.py |
| 53 | +PythonEditor/ui/features/syntaxhighlighter.py |
| 54 | +PythonEditor/ui/ide.py |
| 55 | +PythonEditor/ui/menubar.py |
| 56 | +PythonEditor/ui/pythoneditor.py |
| 57 | +PythonEditor/ui/tabs.py |
| 58 | +PythonEditor/ui/tabview.py |
| 59 | +PythonEditor/ui/terminal.py |
| 60 | +PythonEditor/utils/__init__.py |
| 61 | +PythonEditor/utils/constants.py |
| 62 | +PythonEditor/utils/convert2to3.py |
| 63 | +PythonEditor/utils/debug.py |
| 64 | +PythonEditor/utils/eventfilters.py |
| 65 | +PythonEditor/utils/goto.py |
| 66 | +PythonEditor/utils/introspection.py |
| 67 | +PythonEditor/utils/log.py |
| 68 | +PythonEditor/utils/save.py |
| 69 | +PythonEditor/utils/search.py |
| 70 | +PythonEditor/utils/signals.py |
| 71 | +README.md |
| 72 | +TODO.md |
| 73 | +bin/PythonEditorLaunch.py |
| 74 | +bin/coverage_output.json |
| 75 | +scripts/_init_script.py |
| 76 | +scripts/menu.py |
| 77 | +scripts/prototypes/_popup_label.py |
| 78 | +scripts/prototypes/autocomplete_directconnection.py |
| 79 | +scripts/prototypes/browser.py |
| 80 | +scripts/prototypes/button_tabs.py |
| 81 | +scripts/prototypes/captcha.py |
| 82 | +scripts/prototypes/code_coverage_feature.py |
| 83 | +scripts/prototypes/edittabs.py |
| 84 | +scripts/prototypes/explore_colour_dialog.py |
| 85 | +scripts/prototypes/hover_text_tooltip.py |
| 86 | +scripts/prototypes/jsonhighlight.py |
| 87 | +scripts/prototypes/listtabs.py |
| 88 | +scripts/prototypes/loaderlist.py |
| 89 | +scripts/prototypes/manager.py |
| 90 | +scripts/prototypes/multi_selection.py |
| 91 | +scripts/prototypes/new_autocomplete.py |
| 92 | +scripts/prototypes/new_autocomplete_launch.py |
| 93 | +scripts/prototypes/paint_qtabbar.py |
| 94 | +scripts/prototypes/qtabbar_paint.py |
| 95 | +scripts/prototypes/redirect.py |
| 96 | +scripts/prototypes/shorcut_detector.py |
| 97 | +scripts/prototypes/shortcut_config.json |
| 98 | +scripts/prototypes/shortcut_config.py |
| 99 | +scripts/prototypes/shortcut_conflict.py |
| 100 | +scripts/prototypes/shortcut_functions.py |
| 101 | +scripts/prototypes/shortcutthief.py |
| 102 | +scripts/prototypes/simplemanager.py |
| 103 | +scripts/prototypes/tab_search.py |
| 104 | +scripts/prototypes/tabectomy.py |
| 105 | +scripts/prototypes/tabview_demo.py |
| 106 | +scripts/prototypes/terminal_dict.py |
| 107 | +scripts/prototypes/terminal_earlier.py |
| 108 | +scripts/prototypes/terminal_events.py |
| 109 | +scripts/prototypes/terminal_mid.py |
| 110 | +scripts/resources/qtabbar.py |
| 111 | +scripts/tools/VisualDebug.py |
| 112 | +tests/__init__.py |
| 113 | +tests/in_progress/_autocomplete.py |
| 114 | +tests/in_progress/_autocomplete_keys.py |
| 115 | +tests/in_progress/_key_detector.py |
| 116 | +tests/in_progress/_shortcuts.py |
| 117 | +tests/test_autosavexml.py |
| 118 | +tests/test_dependencies.py |
| 119 | +tests/test_pytestqt/__init__.py |
| 120 | +tests/test_pytestqt/test_actions.py |
| 121 | +tests/test_pytestqt/test_app.py |
| 122 | +tests/test_qt_binding.py |
| 123 | +tests/test_syntax.py |
| 124 | + |
| 125 | +Analysis: |
| 126 | +Okay, I'll help you outline the steps to add this feature to the PythonEditor. This will involve modifications to the UI, core logic, and potentially the autosave system. |
| 127 | + |
| 128 | +Here's a breakdown of the necessary changes and considerations: |
| 129 | + |
| 130 | +**1. UI Modifications (PythonEditor/ui/pythoneditor.py, PythonEditor/ui/editor.py, PythonEditor/ui/Qt.py):** |
| 131 | + |
| 132 | +* **Context Display Area:** Add a `QComboBox` or `QLineEdit` (initially read-only) below the tab bar in `PythonEditor/ui/pythoneditor.py`. This will display the current knob context. Use a `QHBoxLayout` to arrange the display and a button to edit the context. |
| 133 | +* **Edit Context Button:** A `QToolButton` next to the context display area. When clicked, it should enable editing of the knob context. |
| 134 | +* **Signals:** |
| 135 | + * A signal from the `QComboBox` or `QLineEdit` that emits the new knob context (string). |
| 136 | + * A signal from the "edit context" button that toggles the editability of the context display area. |
| 137 | +* **Knob Selection Dialog:** (Optional, but recommended) A custom dialog for selecting a knob from the currently selected Nuke node. This would make it easier for users to find and select the correct knob. This dialog would be launched by a button next to the context display area. |
| 138 | + |
| 139 | +**2. Core Logic Modifications (PythonEditor/core/execute.py, PythonEditor/ui/features/actions.py):** |
| 140 | + |
| 141 | +* **`execute.py`:** Modify the `mainexec` function to accept an optional `knob_context` argument. |
| 142 | + * If `knob_context` is provided, use `nuke.runIn(knob_context, code)` instead of `exec(code)`. |
| 143 | + * Handle potential exceptions that might arise from `nuke.runIn()`, such as the knob not existing or not being valid in the current Nuke context. |
| 144 | +* **`actions.py`:** |
| 145 | + * Modify the `exec_handler` method to retrieve the knob context from the UI. |
| 146 | + * Pass the knob context to `execute.mainexec`. |
| 147 | + * Add methods to: |
| 148 | + * Retrieve the current knob context from the UI. |
| 149 | + * Set the current knob context in the UI. |
| 150 | + * Handle the "edit context" button click (toggle editability). |
| 151 | + * Handle the "knob selection" button click (launch the knob selection dialog). |
| 152 | + |
| 153 | +**3. Data Persistence (PythonEditor/ui/features/autosavexml.py):** |
| 154 | + |
| 155 | +* **XML Schema:** Extend the XML schema in `autosavexml.py` to include a `knob_context` attribute for each `<subscript>` element. |
| 156 | +* **Read/Write:** Modify the `parsexml` and `writexml` functions to read and write the `knob_context` attribute to the XML file. |
| 157 | +* **`AutoSaveManager`:** Update the `AutoSaveManager` class to: |
| 158 | + * Store the `knob_context` in the tab data. |
| 159 | + * Read and write the `knob_context` to the XML file. |
| 160 | + * Update the UI with the `knob_context` when a tab is loaded. |
| 161 | + |
| 162 | +**4. UI Implementation Details:** |
| 163 | + |
| 164 | +* **Knob Selection Dialog:** This dialog could be a custom `QDialog` with a `QTreeView` or `QListView` to display the knobs. It should: |
| 165 | + * Get the currently selected Nuke node using `nuke.selectedNode()`. |
| 166 | + * List the knobs on the node using `node.allKnobs()`. |
| 167 | + * Allow the user to select a knob. |
| 168 | + * Return the `knob.fullyQualifiedName()` of the selected knob. |
| 169 | +* **Error Handling:** Provide informative error messages to the user if: |
| 170 | + * The selected knob does not exist. |
| 171 | + * The code cannot be executed in the given knob context. |
| 172 | + * The external editor path is invalid. |
| 173 | + |
| 174 | +**5. Code Example (Illustrative):** |
| 175 | + |
| 176 | +```python |
| 177 | +# PythonEditor/ui/pythoneditor.py |
| 178 | +# ... (Existing imports) |
| 179 | +from PythonEditor.ui.Qt.QtWidgets import QComboBox, QHBoxLayout, QToolButton |
| 180 | + |
| 181 | +class PythonEditor(QtWidgets.QWidget): |
| 182 | + # ... (Existing code) |
| 183 | + |
| 184 | + def buildUI(self): |
| 185 | + # ... (Existing code) |
| 186 | + |
| 187 | + self.knob_context_combo = QComboBox() |
| 188 | + self.knob_context_combo.setEditable(True) # Allow manual entry |
| 189 | + self.edit_context_button = QToolButton() |
| 190 | + self.edit_context_button.setText("Edit Context") |
| 191 | + self.edit_context_button.setCheckable(True) |
| 192 | + self.edit_context_button.toggled.connect(self.toggle_knob_context_edit) |
| 193 | + |
| 194 | + context_layout = QHBoxLayout() |
| 195 | + context_layout.addWidget(QtWidgets.QLabel("Knob Context:")) |
| 196 | + context_layout.addWidget(self.knob_context_combo) |
| 197 | + context_layout.addWidget(self.edit_context_button) |
| 198 | + |
| 199 | + layout.insertWidget(1, QtWidgets.QWidget()) # insert after menubar |
| 200 | + layout.insertLayout(2, context_layout) # insert after menubar and before the editor. |
| 201 | + |
| 202 | + def toggle_knob_context_edit(self, toggled): |
| 203 | + self.knob_context_combo.setEditable(toggled) |
| 204 | + |
| 205 | + # ... (Rest of the class) |
| 206 | + |
| 207 | +# PythonEditor/ui/features/actions.py |
| 208 | +# ... (Existing imports) |
| 209 | + |
| 210 | +def exec_handler(self): |
| 211 | + """ Handles trigger for execution of code |
| 212 | + (typically Ctrl+Enter). |
| 213 | + If text is selected, call exec on that text. |
| 214 | + If no text is selected, look for cells bordered |
| 215 | + by the symbols #&& and execute text between those |
| 216 | + borders. |
| 217 | + """ |
| 218 | + # ... (Existing code) |
| 219 | + knob_context = self.get_knob_context() |
| 220 | + return self.exec_text(text, whole_text, knob_context=knob_context) |
| 221 | + |
| 222 | +def get_knob_context(self): |
| 223 | + """ Retrieve the knob context from the UI. """ |
| 224 | + return self.pythoneditor.knob_context_combo.currentText() |
| 225 | + |
| 226 | +# PythonEditor/core/execute.py |
| 227 | +def mainexec(text, whole_text, verbosity=VERBOSITY_LOW, knob_context=None): |
| 228 | + """ |
| 229 | + Code execution in top level namespace. |
| 230 | + Reformats exceptions to remove |
| 231 | + references to this file. |
| 232 | +
|
| 233 | + :param text: code to execute |
| 234 | + :param whole_text: all text in document |
| 235 | + :param knob_context: optional knob context to run the code in |
| 236 | + :type text: str |
| 237 | + :type whole_text: str |
| 238 | + :type knob_context: str |
| 239 | + """ |
| 240 | + if len(text.strip().split('\n')) == 1: |
| 241 | + mode = 'single' |
| 242 | + else: |
| 243 | + mode = 'exec' |
| 244 | + try: |
| 245 | + _code = compile(text, FILENAME, mode) |
| 246 | + except SyntaxError: |
| 247 | + error_line_numbers = print_syntax_traceback() |
| 248 | + return error_line_numbers |
| 249 | + |
| 250 | + namespace = __main__.__dict__.copy() |
| 251 | + print('# Result: ') |
| 252 | + try: |
| 253 | + if knob_context: |
| 254 | + import nuke |
| 255 | + nuke.runIn(knob_context, _code) |
| 256 | + else: |
| 257 | + exec(_code, __main__.__dict__) |
| 258 | + except Exception as e: |
| 259 | + error_line_numbers = print_traceback(whole_text, e) |
| 260 | + return error_line_numbers |
| 261 | + else: |
| 262 | + if verbosity == VERBOSITY_LOW: |
| 263 | + return |
| 264 | + # try to print new assigned values |
| 265 | + if mode != 'single': |
| 266 | + return |
| 267 | + |
| 268 | + not_dicts = not all([ |
| 269 | + isinstance(namespace, dict), |
| 270 | + isinstance(__main__.__dict__, dict), |
| 271 | + hasattr(namespace, 'values') |
| 272 | + ]) |
| 273 | + |
| 274 | + if not_dicts: |
| 275 | + return None |
| 276 | + |
| 277 | + try: |
| 278 | + for key, value in __main__.__dict__.items(): |
| 279 | + if value not in namespace.values(): |
| 280 | + try: |
| 281 | + print(value) |
| 282 | + except Exception: |
| 283 | + # if the value throws an error here, |
| 284 | + # try to remove it from globals. |
| 285 | + del __main__.__dict__[key] |
| 286 | + except Exception: |
| 287 | + # if there's an error in iterating through the |
| 288 | + # interpreter globals, restore the globals one |
| 289 | + # by one until the offending value is removed. |
| 290 | + __copy = __main__.__dict__.copy() |
| 291 | + __main__.__dict__.clear() |
| 292 | + for key, value in __copy.items(): |
| 293 | + try: |
| 294 | + __main__.__dict__.update({key:value}) |
| 295 | + except Exception: |
| 296 | + print("Couldn't restore ", key, value, "into main globals") |
| 297 | + |
| 298 | + # TODO: do some logging here. |
| 299 | + # sometimes, this causes |
| 300 | + # SystemError: Objects/longobject.c:244: |
| 301 | + # TypeError('vars() argument must have __dict__ attribute',) |
| 302 | + # bad argument to internal function |
| 303 | + # NotImplementedError, AttributeError, TypeError, SystemError |
| 304 | + return None |
| 305 | +``` |
| 306 | + |
| 307 | +**Key Considerations:** |
| 308 | + |
| 309 | +* **Nuke Dependency:** The `nuke.runIn()` function is specific to Nuke. Make sure this feature is only enabled when running inside Nuke. You can use the `IN_NUKE` constant from `PythonEditor/utils/constants.py` to conditionally enable/disable the UI elements and code paths related to this feature. |
| 310 | +* **Error Handling:** Robust error handling is crucial. The knob context might be invalid, the node might not exist, or the code might still have errors. Catch these exceptions and provide helpful messages to the user. |
| 311 | +* **Performance:** Be mindful of the performance impact of frequently updating the UI, especially when dealing with a large number of knobs. Consider using techniques like debouncing or throttling to limit the frequency of UI updates. |
| 312 | + |
| 313 | +By following these steps, you can add a powerful "run in knob context" feature to the PythonEditor, making it even more useful for Nuke developers. Remember to test thoroughly and handle potential errors gracefully. |
| 314 | + |
0 commit comments