From 748871789a21481d0d55eba4c246272b209f1a8a Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sun, 14 Jul 2024 16:18:31 -0500 Subject: [PATCH 01/24] ! Changed PySide dependency from 5.1x to >=6,<6.8 Fixed UI enough to open Fixed scroll to zoom --- nxt_editor/__init__.py | 28 +++++++++-------------- nxt_editor/dialogs.py | 4 ++-- nxt_editor/dockwidgets/build_view.py | 4 ++-- nxt_editor/dockwidgets/code_editor.py | 2 +- nxt_editor/dockwidgets/find_rep.py | 2 +- nxt_editor/dockwidgets/layer_manager.py | 2 +- nxt_editor/dockwidgets/output_log.py | 2 +- nxt_editor/dockwidgets/property_editor.py | 2 +- nxt_editor/dockwidgets/syntax.py | 14 +++++++----- nxt_editor/dockwidgets/widget_builder.py | 2 +- nxt_editor/node_graphics_item.py | 6 ++--- nxt_editor/stage_view.py | 19 +++++++-------- nxt_editor/user_dir.py | 9 ++++---- setup.py | 2 +- 14 files changed, 48 insertions(+), 50 deletions(-) diff --git a/nxt_editor/__init__.py b/nxt_editor/__init__.py index cf2523c..0a516d8 100644 --- a/nxt_editor/__init__.py +++ b/nxt_editor/__init__.py @@ -36,9 +36,9 @@ class StringSignaler(QtCore.QObject): def make_resources(qrc_path=None, result_path=None): - import PySide2 - pyside_dir = os.path.dirname(PySide2.__file__) - full_pyside2rcc_path = os.path.join(pyside_dir, 'pyside2-rcc') + import PySide6 + import subprocess + pyside_dir = os.path.dirname(PySide6.__file__) full_rcc_path = os.path.join(pyside_dir, 'rcc') this_dir = os.path.dirname(os.path.realpath(__file__)) if not qrc_path: @@ -47,24 +47,15 @@ def make_resources(qrc_path=None, result_path=None): result_path = os.path.join(this_dir, 'qresources.py') msg = 'First launch nxt resource generation from {} to {}' logger.info(msg.format(qrc_path, result_path)) - import subprocess - ver = ['-py2'] - if sys.version_info[0] == 3: - ver += ['-py3'] - args = [qrc_path] + ver + ['-o', result_path] - try: - subprocess.check_call(['pyside2-rcc'] + args) - except: - pass - else: - return + args = [qrc_path, '-o', result_path] try: - subprocess.check_call([full_pyside2rcc_path] + args) + subprocess.check_call(['rcc'] + args) except: pass else: return + try: subprocess.check_call([full_rcc_path, '-g', 'python', qrc_path, '-o', result_path], cwd=pyside_dir) @@ -77,7 +68,7 @@ def make_resources(qrc_path=None, result_path=None): '-o', result_path], cwd=pyside_dir) except: raise Exception("Failed to generate UI resources using pyside2 rcc!" - " Reinstalling pyside2 may fix the problem. If you " + " Reinstalling PySide6 may fix the problem. If you " "know how to use rcc please build from: \"{}\" and " "output to \"{}\"".format(qrc_path, result_path)) else: @@ -129,9 +120,12 @@ def launch_editor(paths=None, start_rpc=True): def show_new_editor(paths=None, start_rpc=True): path = None - if paths is not None: + if paths and isinstance(paths, list): path = paths[0] paths.pop(0) + elif isinstance(paths, str): + path = paths + paths = [] else: paths = [] # Deferred import since main window relies on us diff --git a/nxt_editor/dialogs.py b/nxt_editor/dialogs.py index c6653bc..998edaa 100644 --- a/nxt_editor/dialogs.py +++ b/nxt_editor/dialogs.py @@ -297,7 +297,7 @@ def build_widgets(self): self.save_details_button.released.connect(self.on_save_details) self.detail_buttons_layout = QtWidgets.QHBoxLayout() - self.detail_buttons_layout.addStretch(streth=1) + self.detail_buttons_layout.addStretch(1) self.detail_buttons_layout.addWidget(self.save_details_button) self.detail_buttons_layout.addWidget(self.copy_details_button) @@ -314,7 +314,7 @@ def build_widgets(self): self.top_right_layout = QtWidgets.QVBoxLayout() self.top_right_layout.addWidget(self.text_label) self.top_right_layout.addWidget(self.info_label) - self.top_right_layout.addStretch(streth=1) + self.top_right_layout.addStretch(1) self.top_right_layout.addLayout(self.buttons_layout) self.top_layout = QtWidgets.QHBoxLayout() self.top_layout.addWidget(self.icon) diff --git a/nxt_editor/dockwidgets/build_view.py b/nxt_editor/dockwidgets/build_view.py index 3d21643..768e214 100644 --- a/nxt_editor/dockwidgets/build_view.py +++ b/nxt_editor/dockwidgets/build_view.py @@ -315,7 +315,7 @@ class BuildTable(QtWidgets.QTableView): """ def __init__(self): super(BuildTable, self).__init__() - self.setSelectionMode(self.NoSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.horizontalHeader().hide() self.verticalHeader().hide() self.break_delegate = LetterCheckboxDelegeate('B') @@ -342,7 +342,7 @@ def setModel(self, model): header = self.horizontalHeader() header.setStretchLastSection(False) header.setDefaultSectionSize(28) - header.setSectionResizeMode(header.Fixed) + header.setSectionResizeMode(QtWidgets.QHeaderView.Fixed) if header.count(): column = BuildModel.PATH_COLUMN header.setSectionResizeMode(column, QtWidgets.QHeaderView.Stretch) diff --git a/nxt_editor/dockwidgets/code_editor.py b/nxt_editor/dockwidgets/code_editor.py index 0005dda..307a18c 100644 --- a/nxt_editor/dockwidgets/code_editor.py +++ b/nxt_editor/dockwidgets/code_editor.py @@ -1271,7 +1271,7 @@ def paintEvent(self, event): def get_width(self): count = self.editor.blockCount() - width = self.fontMetrics().width(str(count)) + 10 + width = self.fontMetrics().horizontalAdvance(str(count)) + 10 return width def update_width(self): diff --git a/nxt_editor/dockwidgets/find_rep.py b/nxt_editor/dockwidgets/find_rep.py index bd94f67..23aaa9c 100644 --- a/nxt_editor/dockwidgets/find_rep.py +++ b/nxt_editor/dockwidgets/find_rep.py @@ -196,7 +196,7 @@ def setModel(self, model): super(SearchResultsTree, self).setModel(model) header = self.header() header.setStretchLastSection(False) - header.setSectionResizeMode(header.ResizeToContents) + header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) if self.model(): self.model().modelReset.connect(self.expandAll) diff --git a/nxt_editor/dockwidgets/layer_manager.py b/nxt_editor/dockwidgets/layer_manager.py index c65c9a1..0800400 100644 --- a/nxt_editor/dockwidgets/layer_manager.py +++ b/nxt_editor/dockwidgets/layer_manager.py @@ -100,7 +100,7 @@ def setModel(self, model): header = self.header() header.setStretchLastSection(False) header.setDefaultSectionSize(LayerTreeView.SIZE) - header.setSectionResizeMode(header.Fixed) + header.setSectionResizeMode(QtWidgets.QHeaderView.Fixed) if header.count(): header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) self.hideColumn(LayerModel.TARGET_COLUMN) diff --git a/nxt_editor/dockwidgets/output_log.py b/nxt_editor/dockwidgets/output_log.py index b5ec502..0ec8508 100644 --- a/nxt_editor/dockwidgets/output_log.py +++ b/nxt_editor/dockwidgets/output_log.py @@ -205,7 +205,7 @@ def __init__(self, graph_model=None, parent=None): self.buttons_layout = QtWidgets.QHBoxLayout() self.buttons_layout.addWidget(self.log_filter_button) - self.buttons_layout.addStretch(stretch=1) + self.buttons_layout.addStretch(1) self.buttons_layout.addWidget(self.clear_rich_button) self.rich_output_layout = QtWidgets.QVBoxLayout() diff --git a/nxt_editor/dockwidgets/property_editor.py b/nxt_editor/dockwidgets/property_editor.py index 2f80f38..2c114e0 100644 --- a/nxt_editor/dockwidgets/property_editor.py +++ b/nxt_editor/dockwidgets/property_editor.py @@ -12,7 +12,7 @@ QtCore.QStringListModel except AttributeError: del QtCore - from PySide2 import QtCore + from PySide6 import QtCore # Internal from nxt_editor import user_dir diff --git a/nxt_editor/dockwidgets/syntax.py b/nxt_editor/dockwidgets/syntax.py index 246c23f..aae7750 100644 --- a/nxt_editor/dockwidgets/syntax.py +++ b/nxt_editor/dockwidgets/syntax.py @@ -117,7 +117,7 @@ def highlightBlock(self, text): # Do other syntax formatting for rule in self.rules: expression, nth, formatting = rule - index = expression.indexIn(text, 0) + index = expression.match(text).capturedStart() # This is here because you can't do nested logic in regex nested = 0 if rule in self.special_rules: @@ -151,17 +151,19 @@ def match_multiline(self, text, delimiter, in_state, style): add = 0 # Otherwise, look for the delimiter on this line else: - start = delimiter.indexIn(text) + match = delimiter.match(text) + start = match.capturedStart() # Move past this match - add = delimiter.matchedLength() + add = match.capturedLength() # As long as there's a delimiter match on this line... while start >= 0: + match = delimiter.match(text) # Look for the ending delimiter - end = delimiter.indexIn(text, start + add) + end = match.capturedStart() + (start + add) # Ending delimiter on this line? if end >= add: - length = end - start + add + delimiter.matchedLength() + length = end - start + add + match.capturedLength() self.setCurrentBlockState(0) # No; multi-line string else: @@ -170,7 +172,7 @@ def match_multiline(self, text, delimiter, in_state, style): # Apply formatting self.setFormat(start, length, style) # Look for the next match - start = delimiter.indexIn(text, start + length) + start = match.capturedStart() + (start + length) # Return True if still inside a multi-line string, False otherwise if self.currentBlockState() == in_state: diff --git a/nxt_editor/dockwidgets/widget_builder.py b/nxt_editor/dockwidgets/widget_builder.py index b773b2c..1ecb1a2 100644 --- a/nxt_editor/dockwidgets/widget_builder.py +++ b/nxt_editor/dockwidgets/widget_builder.py @@ -15,7 +15,7 @@ QtCore.QStringListModel except AttributeError: del QtCore - from PySide2 import QtCore + from PySide6 import QtCore # Internal import nxt_editor diff --git a/nxt_editor/node_graphics_item.py b/nxt_editor/node_graphics_item.py index b8de726..5abc1da 100644 --- a/nxt_editor/node_graphics_item.py +++ b/nxt_editor/node_graphics_item.py @@ -9,7 +9,7 @@ from Qt import QtWidgets from Qt import QtGui from Qt import QtCore -from PySide2 import __version_info__ as qt_version +from PySide6 import __version_info__ as qt_version # Internal import nxt_editor @@ -262,13 +262,13 @@ def screen_pos(self): def itemChange(self, change, value): """Override of QtWidgets.QGraphicsItem itemChange.""" # keep connections drawing to node as it moves - if change is self.ItemScenePositionHasChanged: + if change is QtWidgets.QGraphicsItem.ItemPositionChange: graphics = self.view.get_node_connection_graphics(self.node_path) for connection in graphics: connection.rebuild_line() # TODO: Take into account the positions of every selected node and snap them all to a grid as soon as # the user preses shift. This will avoid the weird wavy snapping effect we have right now - if change == self.ItemPositionChange and self.scene(): + if change == QtWidgets.QGraphicsItem.ItemPositionChange and self.scene(): ml = QtWidgets.QApplication.mouseButtons() == QtCore.Qt.LeftButton shift = QtWidgets.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier force_snap = self.view.alignment_actions.snap_action.isChecked() diff --git a/nxt_editor/stage_view.py b/nxt_editor/stage_view.py index 11151ec..dc243f1 100644 --- a/nxt_editor/stage_view.py +++ b/nxt_editor/stage_view.py @@ -8,6 +8,7 @@ from Qt import QtWidgets from Qt import QtGui from Qt import QtCore +from Qt import QtCompat # Interal import nxt_editor @@ -79,8 +80,8 @@ def __init__(self, model, parent=None): self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.horizontalScrollBar().setValue(0) self.verticalScrollBar().setValue(0) - self.setOptimizationFlag(self.DontSavePainterState, enabled=True) - self.setOptimizationFlag(self.DontAdjustForAntialiasing, enabled=True) + self.setOptimizationFlag(QtWidgets.QGraphicsView.DontSavePainterState, enabled=True) + self.setOptimizationFlag(QtWidgets.QGraphicsView.DontAdjustForAntialiasing, enabled=True) # scene self._scene = QtWidgets.QGraphicsScene() self.setScene(self._scene) @@ -700,7 +701,7 @@ def mousePressEvent(self, event): self.zoom_start_pos = event.pos() self._previous_mouse_pos = event.pos() event.accept() - if event.buttons() == QtCore.Qt.LeftButton | QtCore.Qt.MidButton: + if event.buttons() == QtCore.Qt.LeftButton | QtCompat.QtCore.Qt.MidButton: self.zooming = True self.zoom_start_pos = event.pos() self._previous_mouse_pos = event.pos() @@ -747,7 +748,7 @@ def mousePressEvent(self, event): return # block immediate node movement # middle and right button events - elif event.button() == QtCore.Qt.MiddleButton: + elif event.button() == QtCompat.QtCore.Qt.MidButton: # start panning action self.panning = True self._previous_mouse_pos = None @@ -825,11 +826,11 @@ def mouseReleaseEvent(self, event): event.accept() if self.zooming: - if event.buttons() == QtCore.Qt.LeftButton | QtCore.Qt.MidButton: + if event.buttons() == QtCore.Qt.LeftButton | QtCompat.QtCore.Qt.MidButton: self.zooming = False elif event.buttons() == QtCore.Qt.LeftButton: self.zooming = False - elif event.buttons() == QtCore.Qt.MidButton: + elif event.buttons() == QtCompat.QtCore.Qt.MidButton: self.zooming = False if (self._rubber_band_origin is not None and event.button() is QtCore.Qt.LeftButton): @@ -967,7 +968,7 @@ def mouseReleaseEvent(self, event): self.block_context_menu = False self.contextMenuEvent(event) # complete panning action - if self.panning and event.button() == QtCore.Qt.MiddleButton: + if self.panning and event.button() == QtCompat.QtCore.Qt.MidButton: self._previous_mouse_pos = None self.panning = False self._current_pan_distance = 0.0 @@ -993,11 +994,11 @@ def mouseDoubleClickEvent(self, event): item.collapse_node() def wheelEvent(self, event): - self._view_pos = event.pos() + self._view_pos = event.position().toPoint() self._scene_pos = self.mapToScene(self._view_pos) try: - new_scale = event.delta() * .001 + 1.0 + new_scale = event.angleDelta().y() * .001 + 1.0 except AttributeError: new_scale = 1.1 diff --git a/nxt_editor/user_dir.py b/nxt_editor/user_dir.py index 6353f68..2b6c4c9 100644 --- a/nxt_editor/user_dir.py +++ b/nxt_editor/user_dir.py @@ -10,6 +10,7 @@ import json import logging +import shutil import sys if sys.version_info[0] == 2: @@ -209,10 +210,10 @@ def read(self): return try: with open(self.path, 'r+b') as fp: - if sys.version_info[0] == 2: - contents = pickle.load(fp) - else: - contents = pickle.load(fp, encoding='bytes') + contents = pickle.load(fp, encoding='bytes') + except ModuleNotFoundError as e: + if 'PySide2' in e.msg: + shutil.move(self.path, self.path + '.backup') except pickle.UnpicklingError: broken_files.setdefault(self.path, 0) times_hit = broken_files[self.path] diff --git a/setup.py b/setup.py index 7b98ff4..6161dbe 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ python_requires='>=3.7, <3.11', install_requires=['nxt-core<1.0,>=0.14', 'qt.py<3', - 'pyside2>=5.11,<=5.16' + 'PySide6>=6,<6.8' ], package_data={ # covers text nxt files From beb1bcec306326e9de8d850cbf101fa45832898e Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sun, 14 Jul 2024 16:51:02 -0500 Subject: [PATCH 02/24] Fixes to get the code editor open --- nxt_editor/dockwidgets/syntax.py | 9 +++++---- nxt_editor/pixmap_button.py | 2 +- nxt_editor/stage_view.py | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/nxt_editor/dockwidgets/syntax.py b/nxt_editor/dockwidgets/syntax.py index aae7750..5a64f01 100644 --- a/nxt_editor/dockwidgets/syntax.py +++ b/nxt_editor/dockwidgets/syntax.py @@ -117,7 +117,8 @@ def highlightBlock(self, text): # Do other syntax formatting for rule in self.rules: expression, nth, formatting = rule - index = expression.match(text).capturedStart() + match = expression.match(text) + index = match.capturedStart() # This is here because you can't do nested logic in regex nested = 0 if rule in self.special_rules: @@ -126,10 +127,10 @@ def highlightBlock(self, text): while index >= 0: # We actually want the index of the nth match - index = expression.pos(nth) - length = len(expression.cap(nth)) + index = match.capturedStart(nth) + length = len(match.captured(nth)) self.setFormat(index, length + nested, formatting) - index = expression.indexIn(text, index + length) + index = match.capturedStart(text) self.setCurrentBlockState(0) diff --git a/nxt_editor/pixmap_button.py b/nxt_editor/pixmap_button.py index 3239ad4..e4a07f6 100644 --- a/nxt_editor/pixmap_button.py +++ b/nxt_editor/pixmap_button.py @@ -71,7 +71,7 @@ def paintEvent(self, event): pix = self.pixmap_pressed painter = QtGui.QPainter(self) - painter.drawPixmap(event.rect(), pix) + painter.drawPixmap(event.rect(), QtGui.QPixmap(pix)) del painter def enterEvent(self, event): diff --git a/nxt_editor/stage_view.py b/nxt_editor/stage_view.py index dc243f1..b45af64 100644 --- a/nxt_editor/stage_view.py +++ b/nxt_editor/stage_view.py @@ -701,7 +701,7 @@ def mousePressEvent(self, event): self.zoom_start_pos = event.pos() self._previous_mouse_pos = event.pos() event.accept() - if event.buttons() == QtCore.Qt.LeftButton | QtCompat.QtCore.Qt.MidButton: + if event.buttons() == QtCore.Qt.LeftButton | QtCore.Qt.MiddleButton: self.zooming = True self.zoom_start_pos = event.pos() self._previous_mouse_pos = event.pos() @@ -748,7 +748,7 @@ def mousePressEvent(self, event): return # block immediate node movement # middle and right button events - elif event.button() == QtCompat.QtCore.Qt.MidButton: + elif event.button() == QtCore.Qt.MiddleButton: # start panning action self.panning = True self._previous_mouse_pos = None @@ -826,11 +826,11 @@ def mouseReleaseEvent(self, event): event.accept() if self.zooming: - if event.buttons() == QtCore.Qt.LeftButton | QtCompat.QtCore.Qt.MidButton: + if event.buttons() == QtCore.Qt.LeftButton | QtCore.Qt.MiddleButton: self.zooming = False elif event.buttons() == QtCore.Qt.LeftButton: self.zooming = False - elif event.buttons() == QtCompat.QtCore.Qt.MidButton: + elif event.buttons() == QtCore.Qt.MiddleButton: self.zooming = False if (self._rubber_band_origin is not None and event.button() is QtCore.Qt.LeftButton): @@ -968,7 +968,7 @@ def mouseReleaseEvent(self, event): self.block_context_menu = False self.contextMenuEvent(event) # complete panning action - if self.panning and event.button() == QtCompat.QtCore.Qt.MidButton: + if self.panning and event.button() == QtCore.Qt.MiddleButton: self._previous_mouse_pos = None self.panning = False self._current_pan_distance = 0.0 From c5ff6e7e75d300147dc134510528d949e3c36b75 Mon Sep 17 00:00:00 2001 From: Michael Aldrich <11843596+MichaelAldrich@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:09:24 -0400 Subject: [PATCH 03/24] Fix spelling of layer manager title --- nxt_editor/dockwidgets/layer_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxt_editor/dockwidgets/layer_manager.py b/nxt_editor/dockwidgets/layer_manager.py index c65c9a1..4aed6f5 100644 --- a/nxt_editor/dockwidgets/layer_manager.py +++ b/nxt_editor/dockwidgets/layer_manager.py @@ -19,7 +19,7 @@ class LayerManager(DockWidgetBase): """Interactive tree view of the layers in the open graph. """ - def __init__(self, title='Layer Manger', parent=None): + def __init__(self, title='Layer Manager', parent=None): super(LayerManager, self).__init__(title=title, parent=parent, minimum_width=100) From 4bb3406a99c865e3966084b06cf759344f9b265b Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:56:32 -0500 Subject: [PATCH 04/24] * Fixed animations for PySide6 - Removed logic for checking for old versions of PySide --- nxt_editor/node_graphics_item.py | 12 +----------- nxt_editor/stage_view.py | 5 +---- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/nxt_editor/node_graphics_item.py b/nxt_editor/node_graphics_item.py index 5abc1da..128f4b7 100644 --- a/nxt_editor/node_graphics_item.py +++ b/nxt_editor/node_graphics_item.py @@ -9,8 +9,6 @@ from Qt import QtWidgets from Qt import QtGui from Qt import QtCore -from PySide6 import __version_info__ as qt_version - # Internal import nxt_editor from nxt import nxt_path, nxt_node @@ -25,16 +23,8 @@ MIN_LOD = user_prefs.get(USER_PREF.LOD, .4) -_pyside_version = qt_version - - -if _pyside_version[1] < 11: - graphic_type = QtWidgets.QGraphicsItem -else: - graphic_type = QtWidgets.QGraphicsObject - -class NodeGraphicsItem(graphic_type): +class NodeGraphicsItem(QtWidgets.QGraphicsObject): """The graphics item used to represent nodes in the graph. Contains instances of NodeGraphicsPlug for each attribute on the associated node. Contains functionality for arranging children into stacks. diff --git a/nxt_editor/stage_view.py b/nxt_editor/stage_view.py index b45af64..d6ad664 100644 --- a/nxt_editor/stage_view.py +++ b/nxt_editor/stage_view.py @@ -13,8 +13,7 @@ # Interal import nxt_editor from nxt import nxt_node, tokens -from nxt_editor.node_graphics_item import (NodeGraphicsItem, NodeGraphicsPlug, - _pyside_version) +from nxt_editor.node_graphics_item import NodeGraphicsItem, NodeGraphicsPlug from nxt_editor.connection_graphics_item import AttrConnectionGraphic from nxt_editor.dialogs import NxtWarningDialog from nxt_editor.commands import * @@ -43,8 +42,6 @@ def __init__(self, model, parent=None): super(StageView, self).__init__(parent=parent) self.main_window = parent self._do_anim_pref = user_prefs.get(USER_PREF.ANIMATION, True) - if _pyside_version[1] < 11: - self._do_anim_pref = False self.do_animations = self._do_anim_pref self.once_sec_timer = QtCore.QTimer(self) self.once_sec_timer.timeout.connect(self.calculate_fps) From 2e824898c4ead758d930d0e0d228b44926f0be4a Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:59:51 -0500 Subject: [PATCH 05/24] * Raised python ceiling to 3.12 * Preemptively updated syntax escape sequences in prep for 3.12 support --- nxt_editor/dockwidgets/syntax.py | 8 ++++---- setup.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nxt_editor/dockwidgets/syntax.py b/nxt_editor/dockwidgets/syntax.py index 5a64f01..6b3eb07 100644 --- a/nxt_editor/dockwidgets/syntax.py +++ b/nxt_editor/dockwidgets/syntax.py @@ -39,15 +39,15 @@ class PythonHighlighter(QSyntaxHighlighter): # Comparison '==', '!=', '<', '<=', '>', '>=', # Arithmetic - '\+', '-', '\*', '/', '//', '\%', '\*\*', + r'\+', '-', r'\*', '/', '//', r'\%', r'\*\*', # In-place - '\+=', '-=', '\*=', '/=', '\%=', + r'\+=', '-=', r'\*=', '/=', r'\%=', # Bitwise - '\^', '\|', '\&', '\~', '>>', '<<' + r'\^', r'\|', r'\&', r'\~', '>>', '<<' ] # Python braces - braces = ['\{', '\}', '\(', '\)', '\[', '\]'] + braces = [r'\{', r'\}', r'\(', r'\)', r'\[', r'\]'] def __init__(self, document=None): super(PythonHighlighter, self).__init__(document) diff --git a/setup.py b/setup.py index 6161dbe..aab0052 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ long_description_content_type="text/markdown", url="https://github.com/nxt-dev/nxt_editor", packages=setuptools.find_packages(), - python_requires='>=3.7, <3.11', + python_requires='>=3.7, <3.12', install_requires=['nxt-core<1.0,>=0.14', 'qt.py<3', 'PySide6>=6,<6.8' From e61b2885edfa641c0af94f79b25b0f29ff76c7c6 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:00:24 -0500 Subject: [PATCH 06/24] * Raised python ceiling to 3.12 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7b98ff4..cf627d1 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ long_description_content_type="text/markdown", url="https://github.com/nxt-dev/nxt_editor", packages=setuptools.find_packages(), - python_requires='>=3.7, <3.11', + python_requires='>=3.7, <3.12', install_requires=['nxt-core<1.0,>=0.14', 'qt.py<3', 'pyside2>=5.11,<=5.16' From 7cb745279404a6559a9ebdf660a0b395f8070540 Mon Sep 17 00:00:00 2001 From: ImLucasBrown <54835354+imlucasbrown@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:10:11 -0500 Subject: [PATCH 07/24] * Updated auto resource building for PySide6 --- nxt_editor/__init__.py | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/nxt_editor/__init__.py b/nxt_editor/__init__.py index 0a516d8..7aaae07 100644 --- a/nxt_editor/__init__.py +++ b/nxt_editor/__init__.py @@ -36,10 +36,7 @@ class StringSignaler(QtCore.QObject): def make_resources(qrc_path=None, result_path=None): - import PySide6 import subprocess - pyside_dir = os.path.dirname(PySide6.__file__) - full_rcc_path = os.path.join(pyside_dir, 'rcc') this_dir = os.path.dirname(os.path.realpath(__file__)) if not qrc_path: qrc_path = os.path.join(this_dir, 'resources/resources.qrc') @@ -48,26 +45,11 @@ def make_resources(qrc_path=None, result_path=None): msg = 'First launch nxt resource generation from {} to {}' logger.info(msg.format(qrc_path, result_path)) - args = [qrc_path, '-o', result_path] + args = [qrc_path, '-o', result_path, '-g', 'python'] try: - subprocess.check_call(['rcc'] + args) + subprocess.call(['pyside6-rcc'] + args) except: - pass - else: - return - - try: - subprocess.check_call([full_rcc_path, '-g', 'python', qrc_path, - '-o', result_path], cwd=pyside_dir) - except: - pass - else: - return - try: - subprocess.check_call(['rcc', '-g', 'python', qrc_path, - '-o', result_path], cwd=pyside_dir) - except: - raise Exception("Failed to generate UI resources using pyside2 rcc!" + raise Exception("Failed to generate UI resources using PySide rcc!" " Reinstalling PySide6 may fix the problem. If you " "know how to use rcc please build from: \"{}\" and " "output to \"{}\"".format(qrc_path, result_path)) From 3ba049ec62e6adee6dcac8c4e4e31c6f82af7065 Mon Sep 17 00:00:00 2001 From: ImLucasBrown <54835354+imlucasbrown@users.noreply.github.com> Date: Sat, 9 Nov 2024 12:12:50 -0600 Subject: [PATCH 08/24] ... Fixed default font family name to save 300ms on load time and hush a qt warning. --- nxt_editor/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxt_editor/constants.py b/nxt_editor/constants.py index 51c6174..c1bc4ee 100644 --- a/nxt_editor/constants.py +++ b/nxt_editor/constants.py @@ -22,7 +22,7 @@ class EDITOR_VERSION(object): class FONTS(object): - DEFAULT_FAMILY = 'RobotoMono-Regular' + DEFAULT_FAMILY = 'Roboto Mono' DEFAULT_SIZE = 10 From 5c4210b335bee035326b766639379d862cde2dd9 Mon Sep 17 00:00:00 2001 From: ImLucasBrown <54835354+imlucasbrown@users.noreply.github.com> Date: Thu, 14 Nov 2024 18:35:12 -0600 Subject: [PATCH 09/24] ... Fixed crash caused by triple quotes --- nxt_editor/dockwidgets/syntax.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nxt_editor/dockwidgets/syntax.py b/nxt_editor/dockwidgets/syntax.py index 6b3eb07..f5e0f59 100644 --- a/nxt_editor/dockwidgets/syntax.py +++ b/nxt_editor/dockwidgets/syntax.py @@ -155,7 +155,7 @@ def match_multiline(self, text, delimiter, in_state, style): match = delimiter.match(text) start = match.capturedStart() # Move past this match - add = match.capturedLength() + add = match.capturedLength() + 1 # As long as there's a delimiter match on this line... while start >= 0: @@ -173,7 +173,10 @@ def match_multiline(self, text, delimiter, in_state, style): # Apply formatting self.setFormat(start, length, style) # Look for the next match - start = match.capturedStart() + (start + length) + match = delimiter.match(text, start + length) + if not match.hasMatch(): + break + start = match.capturedStart() # Return True if still inside a multi-line string, False otherwise if self.currentBlockState() == in_state: From 15718f68a93393d62d57f708d16d75daf7a9b4d4 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Fri, 15 Nov 2024 08:57:15 -0600 Subject: [PATCH 10/24] fixed QMessageBox button logic --- nxt_editor/dialogs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nxt_editor/dialogs.py b/nxt_editor/dialogs.py index 998edaa..b47bb9d 100644 --- a/nxt_editor/dialogs.py +++ b/nxt_editor/dialogs.py @@ -373,14 +373,16 @@ def show_message(cls, text, info, details=None): class NxtConfirmDialog(QtWidgets.QMessageBox): + Ok = QtWidgets.QMessageBox.StandardButton.Ok + Cancel = QtWidgets.QMessageBox.StandardButton.Cancel def __init__(self, text='Title', info='Confirm something!', button_text=None, icon=QtWidgets.QMessageBox.Icon.Question): """Simple message box used for user confirmation :param text: Title text :param info: Main info text :param button_text: Custom button text dict: - {QtWidgets.QMessageBox.Ok: 'Custom Ok Text', - QtWidgets.QMessageBox.Cancel: 'Custom Cancel Text'} + {QtWidgets.QMessageBox.StandardButton.Ok: 'Custom Ok Text', + QtWidgets.QMessageBox.StandardButton.Cancel: 'Custom Cancel Text'} """ super(NxtConfirmDialog, self).__init__() self.setText(text) From d23efd5a7c19f0f0539848768adcd193ec9ca91b Mon Sep 17 00:00:00 2001 From: Quentin Birrer Date: Fri, 15 Nov 2024 16:09:05 +0100 Subject: [PATCH 11/24] Fix multiline styling. Fix a wheel scroll bug on NxtCodeEditor. --- nxt_editor/dockwidgets/code_editor.py | 2 +- nxt_editor/dockwidgets/syntax.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nxt_editor/dockwidgets/code_editor.py b/nxt_editor/dockwidgets/code_editor.py index 307a18c..dd1b42d 100644 --- a/nxt_editor/dockwidgets/code_editor.py +++ b/nxt_editor/dockwidgets/code_editor.py @@ -778,7 +778,7 @@ def focusOutEvent(self, event): return QtWidgets.QPlainTextEdit.focusOutEvent(self, event) def wheelEvent(self, event): - delta = event.delta() + delta = event.angleDelta().y() / 8 if event.modifiers() == QtCore.Qt.ControlModifier: if delta > 0: self.set_font_size(delta=0.5) diff --git a/nxt_editor/dockwidgets/syntax.py b/nxt_editor/dockwidgets/syntax.py index f5e0f59..4d8d25f 100644 --- a/nxt_editor/dockwidgets/syntax.py +++ b/nxt_editor/dockwidgets/syntax.py @@ -161,7 +161,7 @@ def match_multiline(self, text, delimiter, in_state, style): while start >= 0: match = delimiter.match(text) # Look for the ending delimiter - end = match.capturedStart() + (start + add) + end = match.capturedStart() - (start + add) # Ending delimiter on this line? if end >= add: length = end - start + add + match.capturedLength() From c27cc131d412aa2878760e8ab8c4b20d844bc3c7 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Fri, 22 Nov 2024 08:05:35 -0600 Subject: [PATCH 12/24] * Updated and tested Blender Addon to support Blender 4.x --- nxt_editor/integration/blender/README.md | 1 + nxt_editor/integration/blender/nxt_blender.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nxt_editor/integration/blender/README.md b/nxt_editor/integration/blender/README.md index ce362ef..b59c230 100644 --- a/nxt_editor/integration/blender/README.md +++ b/nxt_editor/integration/blender/README.md @@ -23,6 +23,7 @@ installing Python (same version as Blender's) on your machine._ 1. Launch Blender with elevated permissions 2. Open the addon manager (Edit > Preferences > Add-ons) 3. Click "Install" and select the `nxt_blender.py` file provided with this addon zip + * In newer versions of Blender you need to click the small arrow on the top right and select "Install from Disk". 4. Enable the `NXT Blender` and twirl down the addon preferences 5. Click `Install NXT dependencies` - It is recommended to open the console window before running the script, so you can see what's happening. Window > Toggle System Console. diff --git a/nxt_editor/integration/blender/nxt_blender.py b/nxt_editor/integration/blender/nxt_blender.py index c9f9a2e..c3729cc 100644 --- a/nxt_editor/integration/blender/nxt_blender.py +++ b/nxt_editor/integration/blender/nxt_blender.py @@ -26,7 +26,7 @@ bl_info = { "name": "NXT Blender", "blender": (3, 4, 0), - "version": (0, 3, 0), + "version": (0, 4, 0), "location": "NXT > Open Editor", "wiki_url": "https://nxt-dev.github.io/", "tracker_url": "https://github.com/nxt-dev/nxt_editor/issues", @@ -41,8 +41,9 @@ b_major, b_minor, b_patch = bpy.app.version if b_major == 2: bl_info["blender"] = (2, 80, 0) -elif b_major != 3: - raise RuntimeError('Unsupported major Blender version: {}'.format(b_major)) +elif b_major > 4: + raise RuntimeError('NXT does not support Blender version: ' + '{}.x'.format(b_major)) class BLENDER_PLUGIN_VERSION(object): From 7d0a6ae974953d9725abe42f22e471e0aa814344 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:28:15 -0600 Subject: [PATCH 13/24] ! The RPC server will no longer start by default, to start it you must pass `-rpc` to the CLI or `start_rpc=True` to `show_new_editor`. --- nxt_editor/__init__.py | 4 ++-- nxt_editor/main_window.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nxt_editor/__init__.py b/nxt_editor/__init__.py index 7aaae07..8240de5 100644 --- a/nxt_editor/__init__.py +++ b/nxt_editor/__init__.py @@ -84,7 +84,7 @@ def _new_qapp(): return app -def launch_editor(paths=None, start_rpc=True): +def launch_editor(paths=None, start_rpc=False): """Launch an instance of the editor. Will attach to existing QApp if found, otherwise will create and open one. """ @@ -100,7 +100,7 @@ def launch_editor(paths=None, start_rpc=True): return instance -def show_new_editor(paths=None, start_rpc=True): +def show_new_editor(paths=None, start_rpc=False): path = None if paths and isinstance(paths, list): path = paths[0] diff --git a/nxt_editor/main_window.py b/nxt_editor/main_window.py index 13971e0..a478e59 100644 --- a/nxt_editor/main_window.py +++ b/nxt_editor/main_window.py @@ -52,7 +52,7 @@ class MainWindow(QtWidgets.QMainWindow): new_log_signal = QtCore.Signal(logging.LogRecord) font_size_changed = QtCore.Signal(int) - def __init__(self, filepath=None, parent=None, start_rpc=True): + def __init__(self, filepath=None, parent=None, start_rpc=False): """Create NXT window. :param parent: parent to attach this UI to. From 92f5f0b1c322f855478519e4cf0da2b2b3dcedbd Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sat, 23 Nov 2024 09:55:19 -0600 Subject: [PATCH 14/24] + Added ability to copy preferences from an older version of NXT during UI launch. --- nxt_editor/__init__.py | 2 ++ nxt_editor/constants.py | 6 +++-- nxt_editor/dialogs.py | 26 +++++++++++++++++++++ nxt_editor/user_dir.py | 50 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/nxt_editor/__init__.py b/nxt_editor/__init__.py index 8240de5..b3f71e0 100644 --- a/nxt_editor/__init__.py +++ b/nxt_editor/__init__.py @@ -93,6 +93,8 @@ def launch_editor(paths=None, start_rpc=False): app = existing else: app = _new_qapp() + from nxt_editor.dialogs import CopyPrefsDialogue + CopyPrefsDialogue.show_message() # Show message if upgrade possible instance = show_new_editor(paths, start_rpc) app.setActiveWindow(instance) if not existing: diff --git a/nxt_editor/constants.py b/nxt_editor/constants.py index c1bc4ee..66a3d48 100644 --- a/nxt_editor/constants.py +++ b/nxt_editor/constants.py @@ -26,7 +26,9 @@ class FONTS(object): DEFAULT_SIZE = 10 -_pref_dir_name = str(EDITOR_VERSION.MAJOR) -PREF_DIR = os.path.join(USER_DIR, 'prefs', _pref_dir_name) +PREF_DIR_INT = EDITOR_VERSION.MAJOR +PREF_DIR_NAME = 'prefs' +_pref_dir_num = str(PREF_DIR_INT) +PREF_DIR = os.path.join(USER_DIR, PREF_DIR_NAME, _pref_dir_num) NXT_WEBSITE = 'https://nxt-dev.github.io/' diff --git a/nxt_editor/dialogs.py b/nxt_editor/dialogs.py index b47bb9d..29e1699 100644 --- a/nxt_editor/dialogs.py +++ b/nxt_editor/dialogs.py @@ -410,6 +410,32 @@ def show_message(cls, *args, **kwargs): return False +class CopyPrefsDialogue(NxtConfirmDialog): + title_text = (f'Copy version {user_dir.UPGRADE_PREFS_FROM_VERSION} ' + f'Preferences?') + button_text = { + NxtConfirmDialog.Ok: f'Copy v' + f'{user_dir.UPGRADE_PREFS_FROM_VERSION} prefs', + NxtConfirmDialog.Cancel: 'Use default preferences' + } + info = ('Would you like to copy preferences from an older version of ' + 'NXT?\nSome things like the window layout may not be preserved.') + + def __int__(self): + super(CopyPrefsDialogue, self).__init__(text=self.title_text, + info=self.info, + button_text=self.button_text) + + @classmethod + def show_message(cls): + if not user_dir.UPGRADABLE_PREFS: + return + do_upgrade = super().show_message(text=cls.title_text, info=cls.info, + button_text=cls.button_text) + if do_upgrade: + user_dir.upgrade_prefs() + + class UnsavedLayersDialogue(QtWidgets.QDialog): @classmethod def save_before_exit(cls, stage_models, main_window): diff --git a/nxt_editor/user_dir.py b/nxt_editor/user_dir.py index 2b6c4c9..7a9a937 100644 --- a/nxt_editor/user_dir.py +++ b/nxt_editor/user_dir.py @@ -20,7 +20,7 @@ # Internal from nxt.constants import USER_DIR -from nxt_editor.constants import PREF_DIR +from nxt_editor.constants import PREF_DIR, PREF_DIR_INT, PREF_DIR_NAME import nxt_editor logger = logging.getLogger(nxt_editor.LOGGER_NAME) @@ -32,7 +32,9 @@ SKIPPOINT_FILE = os.path.join(PREF_DIR, 'skippoints') HOTKEYS_PREF = os.path.join(PREF_DIR, 'hotkeys.json') MAX_RECENT_FILES = 10 - +JSON_PREFS = [USER_PREFS_PATH, BREAKPOINT_FILE, SKIPPOINT_FILE, HOTKEYS_PREF] +UPGRADABLE_PREFS = [] +UPGRADE_PREFS_FROM_VERSION = -1 broken_files = {} @@ -48,7 +50,51 @@ def ensure_pref_dir_exists(): raise Exception('Failed to generate user dir {}' + USER_DIR) +def check_for_upgradable_prefs(): + """ + Identify preference files that can be safely upgraded + between major editor versions. Only existing preference files + from the nearest older version are copied; missing files are skipped + without warnings. + """ + global UPGRADABLE_PREFS + global UPGRADE_PREFS_FROM_VERSION + for pref_file in JSON_PREFS: + if os.path.isfile(pref_file): + break + else: # Didn't find any json prefs in current version prefs + dir_num = PREF_DIR_INT - 1 + while dir_num > -1: + old_pref_dir = os.path.join(USER_DIR, PREF_DIR_NAME, str(dir_num)) + for pref_file in JSON_PREFS: + file_name = os.path.basename(pref_file) + old_pref_file = os.path.join(old_pref_dir, file_name) + if os.path.isfile(old_pref_file): + # In the future if we change the structure of the json + # prefs we'll need a way to convert them or skip + UPGRADABLE_PREFS.append(old_pref_file) + if UPGRADABLE_PREFS: + UPGRADE_PREFS_FROM_VERSION = dir_num + break + dir_num -= 1 + + +def upgrade_prefs(): + """ + Copies old 'upgradeable' prefs to current pref dir, will eat and + exception raised by shutil.copy. In the future this function may do more + than simply copy. + """ + for pref_file in UPGRADABLE_PREFS: + try: + shutil.copy(pref_file, PREF_DIR) + except Exception as e: + logger.error(e) + logger.error(f'Failed to copy old pref file: {pref_file}') + + ensure_pref_dir_exists() +check_for_upgradable_prefs() class USER_PREF(): From 1c60f0c2d7db5857572e236346da035745106036 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sat, 23 Nov 2024 09:55:56 -0600 Subject: [PATCH 15/24] ... Fixed Maya plugin to use the correct launch method for the main window. --- nxt_editor/integration/maya/plug-ins/nxt_maya.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxt_editor/integration/maya/plug-ins/nxt_maya.py b/nxt_editor/integration/maya/plug-ins/nxt_maya.py index c42ee49..57fb831 100644 --- a/nxt_editor/integration/maya/plug-ins/nxt_maya.py +++ b/nxt_editor/integration/maya/plug-ins/nxt_maya.py @@ -107,7 +107,7 @@ def doIt(self, args): if __NXT_INSTANCE__: __NXT_INSTANCE__.close() return - nxt_win = nxt_editor.main_window.MainWindow() + nxt_win = nxt_editor.show_new_editor() if 'win32' in sys.platform: # gives nxt it's own entry on taskbar nxt_win.setWindowFlags(QtCore.Qt.Window) From c580cbe414ff060201da3c3b7d8bc0aae01f011b Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sat, 23 Nov 2024 09:57:10 -0600 Subject: [PATCH 16/24] set the window title so it doesn't just say "Python" --- nxt_editor/dialogs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nxt_editor/dialogs.py b/nxt_editor/dialogs.py index 29e1699..d950b94 100644 --- a/nxt_editor/dialogs.py +++ b/nxt_editor/dialogs.py @@ -391,6 +391,7 @@ def __init__(self, text='Title', info='Confirm something!', self.setIcon(icon) self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.setStandardButtons(self.Ok | self.Cancel) + self.setWindowTitle(text) if button_text: self.setButtonText(self.Ok, button_text.get(self.Ok, 'Ok')) self.setButtonText(self.Cancel, button_text.get(self.Cancel, From 6818fc7b766623eecc862f08e2ca84df3c32d056 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sat, 23 Nov 2024 10:41:04 -0600 Subject: [PATCH 17/24] cleaned up the usage of globals refactored names to make more sense --- nxt_editor/__init__.py | 4 ++-- nxt_editor/dialogs.py | 37 ++++++++++++++++++------------------- nxt_editor/user_dir.py | 27 +++++++++++++++------------ 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/nxt_editor/__init__.py b/nxt_editor/__init__.py index b3f71e0..ca824ca 100644 --- a/nxt_editor/__init__.py +++ b/nxt_editor/__init__.py @@ -93,8 +93,8 @@ def launch_editor(paths=None, start_rpc=False): app = existing else: app = _new_qapp() - from nxt_editor.dialogs import CopyPrefsDialogue - CopyPrefsDialogue.show_message() # Show message if upgrade possible + from nxt_editor.dialogs import UpgradePrefsDialogue + UpgradePrefsDialogue.confirm_upgrade_if_possible() instance = show_new_editor(paths, start_rpc) app.setActiveWindow(instance) if not existing: diff --git a/nxt_editor/dialogs.py b/nxt_editor/dialogs.py index d950b94..f56f41a 100644 --- a/nxt_editor/dialogs.py +++ b/nxt_editor/dialogs.py @@ -411,30 +411,29 @@ def show_message(cls, *args, **kwargs): return False -class CopyPrefsDialogue(NxtConfirmDialog): - title_text = (f'Copy version {user_dir.UPGRADE_PREFS_FROM_VERSION} ' - f'Preferences?') - button_text = { - NxtConfirmDialog.Ok: f'Copy v' - f'{user_dir.UPGRADE_PREFS_FROM_VERSION} prefs', - NxtConfirmDialog.Cancel: 'Use default preferences' - } - info = ('Would you like to copy preferences from an older version of ' - 'NXT?\nSome things like the window layout may not be preserved.') - - def __int__(self): - super(CopyPrefsDialogue, self).__init__(text=self.title_text, - info=self.info, - button_text=self.button_text) +class UpgradePrefsDialogue(NxtConfirmDialog): + def __int__(self, title_text, info, button_text): + super(UpgradePrefsDialogue, self).__init__(text=title_text, + info=info, + button_text=button_text) @classmethod - def show_message(cls): + def confirm_upgrade_if_possible(cls): + if not user_dir.UPGRADABLE_PREFS: return - do_upgrade = super().show_message(text=cls.title_text, info=cls.info, - button_text=cls.button_text) + from_version = user_dir.UPGRADE_PREFS_FROM_VERSION + title_text = f'Copy version {from_version} Preferences?' + button_text = { + NxtConfirmDialog.Ok: f'Copy v{from_version} prefs', + NxtConfirmDialog.Cancel: 'Use default preferences' + } + i = ('Would you like to copy preferences from an older version of NXT?' + '\nSome things like the window layout may not be preserved.') + do_upgrade = super().show_message(text=title_text, info=i, + button_text=button_text) if do_upgrade: - user_dir.upgrade_prefs() + user_dir.upgrade_prefs(user_dir.UPGRADABLE_PREFS) class UnsavedLayersDialogue(QtWidgets.QDialog): diff --git a/nxt_editor/user_dir.py b/nxt_editor/user_dir.py index 7a9a937..f0803a8 100644 --- a/nxt_editor/user_dir.py +++ b/nxt_editor/user_dir.py @@ -33,8 +33,6 @@ HOTKEYS_PREF = os.path.join(PREF_DIR, 'hotkeys.json') MAX_RECENT_FILES = 10 JSON_PREFS = [USER_PREFS_PATH, BREAKPOINT_FILE, SKIPPOINT_FILE, HOTKEYS_PREF] -UPGRADABLE_PREFS = [] -UPGRADE_PREFS_FROM_VERSION = -1 broken_files = {} @@ -50,15 +48,17 @@ def ensure_pref_dir_exists(): raise Exception('Failed to generate user dir {}' + USER_DIR) -def check_for_upgradable_prefs(): +def get_upgradable_prefs(): """ Identify preference files that can be safely upgraded between major editor versions. Only existing preference files from the nearest older version are copied; missing files are skipped - without warnings. + without warnings. Returns a list of prefs that can upgrade and the + versio number they're coming from. + :returns: (list, int) """ - global UPGRADABLE_PREFS - global UPGRADE_PREFS_FROM_VERSION + upgradable_prefs = [] + upgrade_prefs_from_version = -1 for pref_file in JSON_PREFS: if os.path.isfile(pref_file): break @@ -72,20 +72,22 @@ def check_for_upgradable_prefs(): if os.path.isfile(old_pref_file): # In the future if we change the structure of the json # prefs we'll need a way to convert them or skip - UPGRADABLE_PREFS.append(old_pref_file) - if UPGRADABLE_PREFS: - UPGRADE_PREFS_FROM_VERSION = dir_num + upgradable_prefs.append(old_pref_file) + if upgradable_prefs: + upgrade_prefs_from_version = dir_num break dir_num -= 1 + return upgradable_prefs, upgrade_prefs_from_version -def upgrade_prefs(): +def upgrade_prefs(prefs_to_upgrade): """ Copies old 'upgradeable' prefs to current pref dir, will eat and exception raised by shutil.copy. In the future this function may do more than simply copy. + :param prefs_to_upgrade: List of pref filepaths to upgrade """ - for pref_file in UPGRADABLE_PREFS: + for pref_file in prefs_to_upgrade: try: shutil.copy(pref_file, PREF_DIR) except Exception as e: @@ -94,7 +96,8 @@ def upgrade_prefs(): ensure_pref_dir_exists() -check_for_upgradable_prefs() +# Must check these before we setup the defaults at the bottom of this file +UPGRADABLE_PREFS, UPGRADE_PREFS_FROM_VERSION = get_upgradable_prefs() class USER_PREF(): From 73224391581340fcd816e4ffe3931964e1f521b7 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Sun, 24 Nov 2024 14:25:07 -0600 Subject: [PATCH 18/24] version up for release --- nxt_editor/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxt_editor/version.json b/nxt_editor/version.json index 1b7135a..1d30317 100644 --- a/nxt_editor/version.json +++ b/nxt_editor/version.json @@ -1,7 +1,7 @@ { "EDITOR": { - "MAJOR": 3, - "MINOR": 16, + "MAJOR": 4, + "MINOR": 0, "PATCH": 0 } } From 861eab73dcd0da2f32e93c4d1eba3257030a2d3b Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Mon, 25 Nov 2024 08:02:36 -0600 Subject: [PATCH 19/24] removed Py2 things from release footer --- build/release_footer.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build/release_footer.md b/build/release_footer.md index b4e7bb2..825cf84 100644 --- a/build/release_footer.md +++ b/build/release_footer.md @@ -6,13 +6,13 @@ This release includes backwards compatibility for graph versions as old as `0.45 # Installation Types Each described installation is self contained, and produces a working nxt. ## Pip Installation -From a Python(2 or 3) environment run the following command: +From a Python 3 environment run the following command: `pip install nxt-editor` **Python Dependancies** - [nxt-core](https://github.com/nxt-dev/nxt) - [Qt.py](https://github.com/mottosso/Qt.py) -- [pyside2](https://doc.qt.io/qtforpython/index.html) - - **Windows Only** Note that pyside2 is not available for python2.7 by default on windows([details](https://wiki.qt.io/Qt_for_Python/Considerations#Missing_Windows_.2F_Python_2.7_release)). For instructions on using conda to build an environment to satifsy these dependencies please see [CONTRIBUTING.md](https://github.com/nxt-dev/nxt/blob/release/CONTRIBUTING.md#python-environment) +- [PySide6](https://doc.qt.io/qtforpython-6/gettingstarted.html) + ## Blender (2.8 and newer) Installation 1. Download Blender addon (nxt_blender.zip) @@ -23,6 +23,6 @@ From a Python(2 or 3) environment run the following command: - By Hand: `/path/to/python.exe -m pip install -U nxt-editor` -## Maya(2019-2020) Installation/Update +## Maya(2019-2025) Installation/Update 1. Download Maya module(nxt_maya.zip) 2. Extract and follow `README.md` inside From 58936f7bab816d900e8bd5990c4b6a3e1000dba5 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Mon, 25 Nov 2024 08:05:45 -0600 Subject: [PATCH 20/24] removed py2 dev env --- nxt_env2.yml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 nxt_env2.yml diff --git a/nxt_env2.yml b/nxt_env2.yml deleted file mode 100644 index ca19199..0000000 --- a/nxt_env2.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: nxt_editor -channels: - - conda-forge -dependencies: - - python=2.7 - - qt.py=1.1 - - pyside2=5.6 - - twine - - requests From 7ab4fb649868a45331210d8214e7cfb01490251f Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Mon, 25 Nov 2024 08:06:02 -0600 Subject: [PATCH 21/24] updated dev env to correct versions --- nxt_env.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nxt_env.yml b/nxt_env.yml index 97d32f0..c2bedc6 100644 --- a/nxt_env.yml +++ b/nxt_env.yml @@ -3,7 +3,7 @@ channels: - conda-forge dependencies: - python=3.9 - - qt.py=1.1 - - pyside2=5.13.2 + - qt.py=1.4 + - pyside2=6.7 - twine - requests From 233fcc305b5b42974ec9a642a3bec7e8f251757d Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Mon, 25 Nov 2024 08:06:33 -0600 Subject: [PATCH 22/24] Updated CONTRIBUTING.md to be accurate for Py3 and PySide6 --- CONTRIBUTING.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f743eb2..6de1fec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,14 +85,9 @@ Conda is best installed via [miniconda](https://docs.conda.io/en/latest/minicond ``` ## Dependencies -- Python 2.7 +- Python 3.9 - [Qt.py](https://github.com/mottosso/Qt.py) - - [PySide2](https://wiki.qt.io/Qt_for_Python) 5.6 (Python 2) - - `pip install -e ` - -- Python 3.7 - - [Qt.py](https://github.com/mottosso/Qt.py) - - [PySide2](https://wiki.qt.io/Qt_for_Python) 5.11.1 (Python 3) + - [PySide6](https://doc.qt.io/qtforpython-6/gettingstarted.html) - `pip install -e ` ## Changelog syntax From e8c4f32d2718180145726ec1919341d1041ce656 Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:52:36 -0600 Subject: [PATCH 23/24] Raised Python supported floor to 3.9 because PySide6 --- CONTRIBUTING.md | 2 +- README.md | 4 ++-- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6de1fec..28edd54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ Conda is best installed via [miniconda](https://docs.conda.io/en/latest/minicond ``` ## Dependencies -- Python 3.9 +- [Python 3.9](https://www.python.org/downloads/release/python-390/) - [Qt.py](https://github.com/mottosso/Qt.py) - [PySide6](https://doc.qt.io/qtforpython-6/gettingstarted.html) - `pip install -e ` diff --git a/README.md b/README.md index a251c64..6bf21d5 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Only clone this repo if you're [contributing](CONTRIBUTING.md) to the NXT codeba
#### Requirements -- Python >= [2.7.*](https://www.python.org/download/releases/2.7) <= [3.7.*](https://www.python.org/download/releases/3.7) -- We strongly recommend using a Python [virtual environment](https://docs.python.org/3.7/tutorial/venv.html) +- Python >= [3.9.*](https://www.python.org/downloads/release/python-390/) +- We strongly recommend using a Python [virtual environment](https://docs.python.org/3/library/venv.html) *[Requirements for contributors](CONTRIBUTING.md#python-environment)* diff --git a/setup.py b/setup.py index aab0052..6574614 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ long_description_content_type="text/markdown", url="https://github.com/nxt-dev/nxt_editor", packages=setuptools.find_packages(), - python_requires='>=3.7, <3.12', + python_requires='>=3.9, <3.12', install_requires=['nxt-core<1.0,>=0.14', 'qt.py<3', 'PySide6>=6,<6.8' From 6567e1c1d2131694d76dc539d9865a25bf808f0e Mon Sep 17 00:00:00 2001 From: Lucas Brown <54835354+imlucasbrown@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:54:57 -0600 Subject: [PATCH 24/24] updated license files --- LICENSE | 2 +- nxt_editor/resources/LICENSE.FEATHERICONS | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index be2edfa..d6a9913 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2020 The nxt Authors +Copyright (c) 2015-2025 The nxt Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, diff --git a/nxt_editor/resources/LICENSE.FEATHERICONS b/nxt_editor/resources/LICENSE.FEATHERICONS index b869713..f26358a 100644 --- a/nxt_editor/resources/LICENSE.FEATHERICONS +++ b/nxt_editor/resources/LICENSE.FEATHERICONS @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2017 Cole Bemis +Copyright (c) 2013-2023 Cole Bemis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal