Code Formatting eric7

Mon, 31 Oct 2022 15:29:18 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 31 Oct 2022 15:29:18 +0100
branch
eric7
changeset 9453
e5065dde905d
parent 9452
325c6de4b1f5
child 9454
f3cf935c0c66

Code Formatting
- added an interface to resort the import statements of Python source files with the 'isort' utility

docs/changelog.md file | annotate | diff | comparison | revisions
eric7.epj file | annotate | diff | comparison | revisions
pyproject.toml file | annotate | diff | comparison | revisions
scripts/install.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackConfigurationDialog.ui file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackDiffWidget.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackDiffWidget.ui file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackFormattingAction.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackFormattingDialog.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackFormattingDialog.ui file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/BlackUtilities.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/FormattingDiffWidget.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/FormattingDiffWidget.ui file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/IsortConfigurationDialog.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/IsortConfigurationDialog.ui file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/IsortFormattingAction.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/IsortFormattingDialog.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/IsortFormattingDialog.ui file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/IsortUtilities.py file | annotate | diff | comparison | revisions
src/eric7/CodeFormatting/__init__.py file | annotate | diff | comparison | revisions
src/eric7/Documentation/Source/eric7.CodeFormatting.BlackDiffWidget.html file | annotate | diff | comparison | revisions
src/eric7/Documentation/Source/eric7.CodeFormatting.FormattingDiffWidget.html file | annotate | diff | comparison | revisions
src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py file | annotate | diff | comparison | revisions
src/eric7/Project/Project.py file | annotate | diff | comparison | revisions
src/eric7/Project/ProjectSourcesBrowser.py file | annotate | diff | comparison | revisions
src/eric7/QScintilla/DocstringGenerator/PyDocstringGenerator.py file | annotate | diff | comparison | revisions
src/eric7/QScintilla/Editor.py file | annotate | diff | comparison | revisions
--- a/docs/changelog.md	Mon Oct 31 14:09:07 2022 +0100
+++ b/docs/changelog.md	Mon Oct 31 15:29:18 2022 +0100
@@ -2,6 +2,9 @@
 
 ### Version 22.12
 - bug fixes
+- Code Formatting
+    - added an interface to resort the import statements of Python source files with
+      the 'isort' utility
 - Previewers
     - added a button to copy the contents of the HTML previewer to the clipboard
 
--- a/eric7.epj	Mon Oct 31 14:09:07 2022 +0100
+++ b/eric7.epj	Mon Oct 31 15:29:18 2022 +0100
@@ -274,8 +274,10 @@
     },
     "FORMS": [
       "src/eric7/CodeFormatting/BlackConfigurationDialog.ui",
-      "src/eric7/CodeFormatting/BlackDiffWidget.ui",
       "src/eric7/CodeFormatting/BlackFormattingDialog.ui",
+      "src/eric7/CodeFormatting/FormattingDiffWidget.ui",
+      "src/eric7/CodeFormatting/IsortConfigurationDialog.ui",
+      "src/eric7/CodeFormatting/IsortFormattingDialog.ui",
       "src/eric7/CondaInterface/CondaExecDialog.ui",
       "src/eric7/CondaInterface/CondaExportDialog.ui",
       "src/eric7/CondaInterface/CondaInfoDialog.ui",
@@ -994,6 +996,32 @@
       },
       "RadonCodeMetrics": {
         "ExcludeFiles": "*/ThirdParty/*, */coverage/*, Ui_*.py, *_rc.py"
+      },
+      "isort": {
+        "config_source": "project",
+        "extend_skip_glob": [
+          "*/Examples/*",
+          "*/ThirdParty/*",
+          "*/coverage/*",
+          "*/Ui_*.py",
+          "*/pycodestyle.py",
+          "*/pyflakes/checker.py",
+          "*/mccabe.py",
+          "*/eradicate.py",
+          "*/ast_unparse.py",
+          "*/piplicenses.py",
+          "*/pipdeptree.py"
+        ],
+        "lines_between_types": 1,
+        "profile": "black",
+        "sort_order": "natural",
+        "supported_extensions": [
+          "py",
+          "pyi",
+          "pyx",
+          "pxd",
+          "pyw"
+        ]
       }
     },
     "PACKAGERSPARMS": {},
@@ -1023,10 +1051,14 @@
       "setup.py",
       "src/__init__.py",
       "src/eric7/CodeFormatting/BlackConfigurationDialog.py",
-      "src/eric7/CodeFormatting/BlackDiffWidget.py",
       "src/eric7/CodeFormatting/BlackFormattingAction.py",
       "src/eric7/CodeFormatting/BlackFormattingDialog.py",
       "src/eric7/CodeFormatting/BlackUtilities.py",
+      "src/eric7/CodeFormatting/FormattingDiffWidget.py",
+      "src/eric7/CodeFormatting/IsortConfigurationDialog.py",
+      "src/eric7/CodeFormatting/IsortFormattingAction.py",
+      "src/eric7/CodeFormatting/IsortFormattingDialog.py",
+      "src/eric7/CodeFormatting/IsortUtilities.py",
       "src/eric7/CodeFormatting/__init__.py",
       "src/eric7/CondaInterface/Conda.py",
       "src/eric7/CondaInterface/CondaExecDialog.py",
--- a/pyproject.toml	Mon Oct 31 14:09:07 2022 +0100
+++ b/pyproject.toml	Mon Oct 31 15:29:18 2022 +0100
@@ -69,6 +69,7 @@
     "cyclonedx-bom",
     "trove-classifiers",
     "black>=22.6.0",
+    "isort>=5.10.0",
     "pywin32>=1.0;platform_system=='Windows'",
 ]
 dynamic = ["version"]
--- a/scripts/install.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/scripts/install.py	Mon Oct 31 15:29:18 2022 +0100
@@ -1698,6 +1698,7 @@
         "cyclonedx-bom": ("cyclonedx_py", ""),
         "trove-classifiers": ("trove_classifiers", ""),
         "black": ("black", ">=22.6.0"),
+        "isort": ("isort", ">=5.10.0"),
     }
     optionalModulesList = {
         # key is pip project name
--- a/src/eric7/CodeFormatting/BlackConfigurationDialog.ui	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/CodeFormatting/BlackConfigurationDialog.ui	Mon Oct 31 15:29:18 2022 +0100
@@ -194,8 +194,8 @@
    <slot>accept()</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>248</x>
-     <y>254</y>
+     <x>257</x>
+     <y>490</y>
     </hint>
     <hint type="destinationlabel">
      <x>157</x>
@@ -210,8 +210,8 @@
    <slot>reject()</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>316</x>
-     <y>260</y>
+     <x>325</x>
+     <y>490</y>
     </hint>
     <hint type="destinationlabel">
      <x>286</x>
--- a/src/eric7/CodeFormatting/BlackDiffWidget.py	Mon Oct 31 14:09:07 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing a window to show a unified diff..
-"""
-
-from PyQt6.QtWidgets import QWidget
-
-from .Ui_BlackDiffWidget import Ui_BlackDiffWidget
-
-from eric7.UI.DiffHighlighter import DiffHighlighter
-
-from eric7 import Preferences
-
-
-class BlackDiffWidget(QWidget, Ui_BlackDiffWidget):
-    """
-    Class implementing a window to show a unified diff..
-    """
-
-    def __init__(self, parent=None):
-        """
-        Constructor
-
-        @param parent reference to the parent widget (defaults to None)
-        @type QWidget (optional)
-        """
-        super().__init__(parent)
-        self.setupUi(self)
-
-        font = Preferences.getEditorOtherFonts("MonospacedFont")
-        self.diffEdit.document().setDefaultFont(font)
-
-        self.__highlighter = DiffHighlighter(self.diffEdit.document())
-        self.__savedGeometry = None
-
-    def showDiff(self, diff):
-        """
-        Public method to show the given diff.
-
-        @param diff text containing the unified diff
-        @type str
-        """
-        self.diffEdit.clear()
-        self.__highlighter.regenerateRules()
-
-        if diff:
-            self.diffEdit.setPlainText(diff)
-        else:
-            self.diffEdit.setPlainText(self.tr("There is no difference."))
-
-        if self.__savedGeometry is not None:
-            self.restoreGeometry(self.__savedGeometry)
-
-        self.show()
-
-    def closeEvent(self, evt):
-        """
-        Protected slot implementing a close event handler.
-
-        @param evt reference to the close event
-        @type QCloseEvent
-        """
-        self.__savedGeometry = self.saveGeometry()
--- a/src/eric7/CodeFormatting/BlackDiffWidget.ui	Mon Oct 31 14:09:07 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>BlackDiffWidget</class>
- <widget class="QWidget" name="BlackDiffWidget">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>700</width>
-    <height>700</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Reformatting Differences</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <widget class="QPlainTextEdit" name="diffEdit">
-     <property name="lineWrapMode">
-      <enum>QPlainTextEdit::NoWrap</enum>
-     </property>
-     <property name="readOnly">
-      <bool>true</bool>
-     </property>
-     <property name="tabStopDistance">
-      <double>8.000000000000000</double>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Close</set>
-     </property>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>rejected()</signal>
-   <receiver>BlackDiffWidget</receiver>
-   <slot>close()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>447</x>
-     <y>680</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>598</x>
-     <y>639</y>
-    </hint>
-   </hints>
-  </connection>
- </connections>
-</ui>
--- a/src/eric7/CodeFormatting/BlackFormattingAction.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/CodeFormatting/BlackFormattingAction.py	Mon Oct 31 15:29:18 2022 +0100
@@ -4,7 +4,7 @@
 #
 
 """
-Module implementing an enum defining the various code formatting actions.
+Module implementing an enum defining the various Black code formatting actions.
 """
 
 import enum
@@ -12,7 +12,7 @@
 
 class BlackFormattingAction(enum.Enum):
     """
-    Class defining the various code formatting actions.
+    Class defining the various Black code formatting actions.
     """
 
     Format = 0
--- a/src/eric7/CodeFormatting/BlackFormattingDialog.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/CodeFormatting/BlackFormattingDialog.py	Mon Oct 31 15:29:18 2022 +0100
@@ -4,7 +4,7 @@
 #
 
 """
-Module implementing a dialog showing the code formatting progress and the result.
+Module implementing a dialog showing the Black code formatting progress and the results.
 """
 
 import copy
@@ -30,7 +30,7 @@
 from .Ui_BlackFormattingDialog import Ui_BlackFormattingDialog
 
 from . import BlackUtilities
-from .BlackDiffWidget import BlackDiffWidget
+from .FormattingDiffWidget import FormattingDiffWidget
 from .BlackFormattingAction import BlackFormattingAction
 
 from eric7 import Preferences, Utilities
@@ -38,7 +38,8 @@
 
 class BlackFormattingDialog(QDialog, Ui_BlackFormattingDialog):
     """
-    Class implementing a dialog showing the code formatting progress and the result.
+    Class implementing a dialog showing the Black code formatting progress and the
+    results.
     """
 
     DataTypeRole = Qt.ItemDataRole.UserRole
@@ -98,7 +99,7 @@
 
     def __performAction(self):
         """
-        Private method to exceute the requested formatting action.
+        Private method to execute the requested formatting action.
         """
         self.progressBar.setValue(0)
         self.progressBar.setVisible(True)
@@ -277,7 +278,7 @@
             )
         elif dataType == "diff":
             if self.__diffDialog is None:
-                self.__diffDialog = BlackDiffWidget()
+                self.__diffDialog = FormattingDiffWidget()
             self.__diffDialog.showDiff(item.data(0, BlackFormattingDialog.DataRole))
 
     def __formatManyFiles(self, files):
@@ -520,8 +521,8 @@
         """
         Private slot to handle the result of a black reformatting action.
 
-        @param status status of the performed action (one of 'changed', 'unchanged',
-            'unmodified', 'failed' or 'ignored')
+        @param status status of the performed action (one of 'changed', 'failed',
+            'ignored', 'unchanged' or 'unmodified')
         @type str
         @param filename name of the processed file
         @type str
--- a/src/eric7/CodeFormatting/BlackFormattingDialog.ui	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/CodeFormatting/BlackFormattingDialog.ui	Mon Oct 31 15:29:18 2022 +0100
@@ -240,8 +240,8 @@
   </layout>
  </widget>
  <tabstops>
+  <tabstop>resultsList</tabstop>
   <tabstop>statusFilterComboBox</tabstop>
-  <tabstop>resultsList</tabstop>
  </tabstops>
  <resources/>
  <connections/>
--- a/src/eric7/CodeFormatting/BlackUtilities.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/CodeFormatting/BlackUtilities.py	Mon Oct 31 15:29:18 2022 +0100
@@ -98,6 +98,6 @@
             "BlackUtilities",
             """<p><b>Black Version {0}</b></p>"""
             """<p><i>Black</i> is the uncompromising Python code"""
-            """ formatter.</p>"""
+            """ formatter.</p>""",
         ).format(black.__version__),
     )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/FormattingDiffWidget.py	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a window to show a unified diff..
+"""
+
+from PyQt6.QtWidgets import QWidget
+
+from .Ui_FormattingDiffWidget import Ui_FormattingDiffWidget
+
+from eric7.UI.DiffHighlighter import DiffHighlighter
+
+from eric7 import Preferences
+
+
+class FormattingDiffWidget(QWidget, Ui_FormattingDiffWidget):
+    """
+    Class implementing a window to show a unified diff..
+    """
+
+    def __init__(self, parent=None):
+        """
+        Constructor
+
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+        font = Preferences.getEditorOtherFonts("MonospacedFont")
+        self.diffEdit.document().setDefaultFont(font)
+
+        self.__highlighter = DiffHighlighter(self.diffEdit.document())
+        self.__savedGeometry = None
+
+    def showDiff(self, diff):
+        """
+        Public method to show the given diff.
+
+        @param diff text containing the unified diff
+        @type str
+        """
+        self.diffEdit.clear()
+        self.__highlighter.regenerateRules()
+
+        if diff:
+            self.diffEdit.setPlainText(diff)
+        else:
+            self.diffEdit.setPlainText(self.tr("There is no difference."))
+
+        if self.__savedGeometry is not None:
+            self.restoreGeometry(self.__savedGeometry)
+
+        self.show()
+
+    def closeEvent(self, evt):
+        """
+        Protected slot implementing a close event handler.
+
+        @param evt reference to the close event
+        @type QCloseEvent
+        """
+        self.__savedGeometry = self.saveGeometry()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/FormattingDiffWidget.ui	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FormattingDiffWidget</class>
+ <widget class="QWidget" name="FormattingDiffWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>700</width>
+    <height>700</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Reformatting Differences</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QPlainTextEdit" name="diffEdit">
+     <property name="lineWrapMode">
+      <enum>QPlainTextEdit::NoWrap</enum>
+     </property>
+     <property name="readOnly">
+      <bool>true</bool>
+     </property>
+     <property name="tabStopDistance">
+      <double>8.000000000000000</double>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FormattingDiffWidget</receiver>
+   <slot>close()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>447</x>
+     <y>680</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>598</x>
+     <y>639</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/IsortConfigurationDialog.py	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,427 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to enter the parameters for an isort formatting run.
+"""
+
+import contextlib
+import copy
+import pathlib
+
+from PyQt6.QtCore import pyqtSlot
+from PyQt6.QtGui import QGuiApplication
+from PyQt6.QtWidgets import QDialog, QDialogButtonBox
+
+from eric7.EricWidgets import EricMessageBox
+from eric7.EricWidgets.EricApplication import ericApp
+
+from isort import Config
+from isort.profiles import profiles
+from isort.settings import VALID_PY_TARGETS
+from isort.wrap_modes import WrapModes
+
+import tomlkit
+
+
+from .Ui_IsortConfigurationDialog import Ui_IsortConfigurationDialog
+
+
+class IsortConfigurationDialog(QDialog, Ui_IsortConfigurationDialog):
+    """
+    Class implementing a dialog to enter the parameters for an isort formatting run.
+    """
+
+    def __init__(self, withProject=True, onlyProject=False, parent=None):
+        """
+        Constructor
+
+        @param withProject flag indicating to look for project configurations
+            (defaults to True)
+        @type bool (optional)
+        @param onlyProject flag indicating to only look for project configurations
+            (defaults to False)
+        @type bool (optional)
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+        self.profileComboBox.lineEdit().setClearButtonEnabled(True)
+
+        self.__parameterWidgetMapping = {
+            "profile": self.profileComboBox,
+            "py_version": self.pythonComboBox,
+            "multi_line_output": self.multiLineComboBox,
+            "sort_order": self.sortOrderComboBox,
+            "supported_extensions": self.extensionsEdit,
+            "line_length": self.lineLengthSpinBox,
+            "lines_before_imports": self.linesBeforeImportsSpinBox,
+            "lines_after_imports": self.linesAfterImportsSpinBox,
+            "lines_between_sections": self.linesBetweenSectionsSpinBox,
+            "lines_between_types": self.linesBetweenTypesSpinBox,
+            "include_trailing_comma": self.trailingCommaCheckBox,
+            "use_parentheses": self.parenthesesCheckBox,
+            "sections": self.sectionsEdit,
+            "extend_skip_glob": self.excludeEdit,
+            "case_sensitive": self.sortCaseSensitiveCheckBox,
+        }
+
+        self.__project = (
+            ericApp().getObject("Project") if (withProject or onlyProject) else None
+        )
+        self.__onlyProject = onlyProject
+
+        self.__pyprojectData = {}
+        self.__projectData = {}
+
+        self.__defaultConfig = Config()
+
+        self.__tomlButton = self.buttonBox.addButton(
+            self.tr("Generate TOML"), QDialogButtonBox.ButtonRole.ActionRole
+        )
+        self.__tomlButton.setToolTip(
+            self.tr("Place a code snippet for 'pyproject.toml' into the clipboard.")
+        )
+        self.__tomlButton.clicked.connect(self.__createTomlSnippet)
+
+        self.profileComboBox.addItem("")
+        self.profileComboBox.addItems(sorted(profiles.keys()))
+
+        self.pythonComboBox.addItem("", "")
+        self.pythonComboBox.addItem(self.tr("All Versions"), "all")
+        for pyTarget in VALID_PY_TARGETS:
+            if pyTarget.startswith("3"):
+                self.pythonComboBox.addItem(
+                    self.tr("Python {0}").format(pyTarget)
+                    if len(pyTarget) == 1
+                    else self.tr("Python {0}.{1}").format(pyTarget[0], pyTarget[1:]),
+                    pyTarget,
+                )
+
+        self.sortOrderComboBox.addItem("", "")
+        self.sortOrderComboBox.addItem("Natural", "natural")
+        self.sortOrderComboBox.addItem("Native Python", "native")
+
+        self.__populateMultiLineComboBox()
+
+        # setup the source combobox
+        self.sourceComboBox.addItem("", "")
+        if self.__project:
+            pyprojectPath = (
+                pathlib.Path(self.__project.getProjectPath()) / "pyproject.toml"
+            )
+            if pyprojectPath.exists():
+                with contextlib.suppress(tomlkit.exceptions.ParseError, OSError):
+                    with pyprojectPath.open("r", encoding="utf-8") as f:
+                        data = tomlkit.load(f)
+                    config = data.get("tool", {}).get("isort", {})
+                if config:
+                    self.__pyprojectData = {
+                        k.replace("--", ""): v for k, v in config.items()
+                    }
+                    self.sourceComboBox.addItem("pyproject.toml", "pyproject")
+            if self.__project.getData("OTHERTOOLSPARMS", "isort") is not None:
+                self.__projectData = copy.deepcopy(
+                    self.__project.getData("OTHERTOOLSPARMS", "isort")
+                )
+                self.sourceComboBox.addItem(self.tr("Project File"), "project")
+            elif onlyProject:
+                self.sourceComboBox.addItem(self.tr("Project File"), "project")
+        if not onlyProject:
+            self.sourceComboBox.addItem(self.tr("Defaults"), "default")
+            self.sourceComboBox.addItem(self.tr("Configuration Below"), "dialog")
+
+        if self.__projectData:
+            source = self.__projectData.get("config_source", "")
+            self.sourceComboBox.setCurrentIndex(self.sourceComboBox.findData(source))
+        elif onlyProject:
+            self.sourceComboBox.setCurrentIndex(self.sourceComboBox.findData("project"))
+
+    def __populateMultiLineComboBox(self):
+        """
+        Private method to populate the multi line output selector.
+        """
+        self.multiLineComboBox.addItem("", -1)
+        for entry, wrapMode in (
+            (self.tr("Grid"), WrapModes.GRID),
+            (self.tr("Vertical"), WrapModes.VERTICAL),
+            (self.tr("Hanging Indent"), WrapModes.HANGING_INDENT),
+            (
+                self.tr("Vertical Hanging Indent"),
+                WrapModes.VERTICAL_HANGING_INDENT,
+            ),
+            (self.tr("Hanging Grid"), WrapModes.VERTICAL_GRID),
+            (self.tr("Hanging Grid Grouped"), WrapModes.VERTICAL_GRID_GROUPED),
+            (self.tr("NOQA"), WrapModes.NOQA),
+            (
+                self.tr("Vertical Hanging Indent Bracket"),
+                WrapModes.VERTICAL_HANGING_INDENT_BRACKET,
+            ),
+            (
+                self.tr("Vertical Prefix From Module Import"),
+                WrapModes.VERTICAL_PREFIX_FROM_MODULE_IMPORT,
+            ),
+            (
+                self.tr("Hanging Indent With Parentheses"),
+                WrapModes.HANGING_INDENT_WITH_PARENTHESES,
+            ),
+            (self.tr("Backslash Grid"), WrapModes.BACKSLASH_GRID),
+        ):
+            self.multiLineComboBox.addItem(entry, wrapMode.value)
+
+    def __loadConfiguration(self, confDict):
+        """
+        Private method to load the configuration section with data of the given
+        dictionary.
+
+        Note: Default values will be loaded for missing parameters.
+
+        @param confDict reference to the data to be loaded
+        @type dict
+        """
+        self.pythonComboBox.setCurrentIndex(
+            self.pythonComboBox.findData(
+                str(confDict["py_version"])
+                if "py_version" in confDict
+                else self.__defaultConfig.py_version.replace("py", "")
+            )
+        )
+        self.multiLineComboBox.setCurrentIndex(
+            self.multiLineComboBox.findData(
+                int(confDict["multi_line_output"])
+                if "multi_line_output" in confDict
+                else self.__defaultConfig.multi_line_output.value
+            )
+        )
+        self.sortOrderComboBox.setCurrentIndex(
+            self.sortOrderComboBox.findData(
+                str(confDict["sort_order"])
+                if "sort_order" in confDict
+                else self.__defaultConfig.sort_order
+            )
+        )
+        self.extensionsEdit.setText(
+            " ".join(
+                confDict["supported_extensions"]
+                if "supported_extensions" in confDict
+                else self.__defaultConfig.supported_extensions
+            )
+        )
+        for parameter in (
+            "line_length",
+            "lines_before_imports",
+            "lines_after_imports",
+            "lines_between_sections",
+            "lines_between_types",
+        ):
+            # set spin box values
+            self.__parameterWidgetMapping[parameter].setValue(
+                confDict[parameter]
+                if parameter in confDict
+                else getattr(self.__defaultConfig, parameter)
+            )
+        for parameter in (
+            "include_trailing_comma",
+            "use_parentheses",
+            "case_sensitive",
+        ):
+            # set check box values
+            self.__parameterWidgetMapping[parameter].setChecked(
+                confDict[parameter]
+                if parameter in confDict
+                else getattr(self.__defaultConfig, parameter)
+            )
+        for parameter in (
+            "sections",
+            "extend_skip_glob",
+        ):
+            # set the plain text edits
+            self.__parameterWidgetMapping[parameter].setPlainText(
+                "\n".join(
+                    confDict[parameter]
+                    if parameter in confDict
+                    else getattr(self.__defaultConfig, parameter)
+                )
+            )
+        # set the profile combo box last because it may change other entries
+        self.profileComboBox.setEditText(
+            confDict["profile"]
+            if "profile" in confDict
+            else self.__defaultConfig.profile
+        )
+
+    @pyqtSlot(str)
+    def on_sourceComboBox_currentTextChanged(self, selection):
+        """
+        Private slot to handle the selection of a configuration source.
+
+        @param selection text of the currently selected item
+        @type str
+        """
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setEnabled(
+            bool(selection) or self.__onlyProject
+        )
+
+        source = self.sourceComboBox.currentData()
+        if source != "dialog":
+            # reset the profile combo box first
+            self.profileComboBox.setCurrentIndex(0)
+
+        if source == "pyproject":
+            self.__loadConfiguration(self.__pyprojectData)
+        elif source == "project":
+            self.__loadConfiguration(self.__projectData)
+        elif source == "default":
+            self.__loadConfiguration({})  # loads the default values
+        elif source == "dialog":
+            # just leave the current entries
+            pass
+
+    @pyqtSlot(str)
+    def on_profileComboBox_editTextChanged(self, profileName):
+        """
+        Private slot to react upon changes of the selected/entered profile.
+
+        @param profileName name of the current profile
+        @type str
+        """
+        if profileName and profileName in profiles:
+            confDict = self.__getConfigurationDict()
+            confDict["profile"] = profileName
+            confDict.update(profiles[profileName])
+            self.__loadConfiguration(confDict)
+
+            for parameter in self.__parameterWidgetMapping:
+                self.__parameterWidgetMapping[parameter].setEnabled(
+                    parameter not in profiles[profileName]
+                )
+        else:
+            for widget in self.__parameterWidgetMapping.values():
+                widget.setEnabled(True)
+
+    @pyqtSlot()
+    def __createTomlSnippet(self):
+        """
+        Private slot to generate a TOML snippet of the current configuration.
+
+        Note: Only non-default values are included in this snippet.
+
+        The code snippet is copied to the clipboard and may be placed inside the
+        'pyproject.toml' file.
+        """
+        configDict = self.__getConfigurationDict()
+
+        isort = tomlkit.table()
+        for key, value in configDict.items():
+            isort[key] = value
+
+        doc = tomlkit.document()
+        doc["tool"] = tomlkit.table(is_super_table=True)
+        doc["tool"]["isort"] = isort
+
+        QGuiApplication.clipboard().setText(tomlkit.dumps(doc))
+
+        EricMessageBox.information(
+            self,
+            self.tr("Create TOML snippet"),
+            self.tr(
+                """The 'pyproject.toml' snippet was copied to the clipboard"""
+                """ successfully."""
+            ),
+        )
+
+    def __getConfigurationDict(self):
+        """
+        Private method to assemble and return a dictionary containing the entered
+        non-default configuration parameters.
+
+        @return dictionary containing the non-default configuration parameters
+        @rtype dict
+        """
+        configDict = {}
+
+        if self.profileComboBox.currentText():
+            configDict["profile"] = self.profileComboBox.currentText()
+        if (
+            self.pythonComboBox.currentText()
+            and self.pythonComboBox.currentData()
+            != self.__defaultConfig.py_version.replace("py", "")
+        ):
+            configDict["py_version"] = self.pythonComboBox.currentData()
+        if self.multiLineComboBox.isEnabled() and self.multiLineComboBox.currentText():
+            configDict["multi_line_output"] = self.multiLineComboBox.currentData()
+        if self.sortOrderComboBox.isEnabled() and self.sortOrderComboBox.currentText():
+            configDict["sort_order"] = self.sortOrderComboBox.currentData()
+        if self.extensionsEdit.isEnabled() and self.extensionsEdit.text():
+            configDict["supported_extensions"] = [
+                e.lstrip(".")
+                for e in self.extensionsEdit.text().strip().split()
+                if e.lstrip(".")
+            ]
+
+        for parameter in (
+            "line_length",
+            "lines_before_imports",
+            "lines_after_imports",
+            "lines_between_sections",
+            "lines_between_types",
+        ):
+            if self.__parameterWidgetMapping[
+                parameter
+            ].isEnabled() and self.__parameterWidgetMapping[
+                parameter
+            ].value() != getattr(
+                self.__defaultConfig, parameter
+            ):
+                configDict[parameter] = self.__parameterWidgetMapping[parameter].value()
+
+        for parameter in (
+            "include_trailing_comma",
+            "use_parentheses",
+            "case_sensitive",
+        ):
+            if self.__parameterWidgetMapping[
+                parameter
+            ].isEnabled() and self.__parameterWidgetMapping[
+                parameter
+            ].isChecked() != getattr(
+                self.__defaultConfig, parameter
+            ):
+                configDict[parameter] = self.__parameterWidgetMapping[
+                    parameter
+                ].isChecked()
+
+        for parameter in (
+            "sections",
+            "extend_skip_glob",
+        ):
+            if self.__parameterWidgetMapping[parameter].isEnabled():
+                value = (
+                    self.__parameterWidgetMapping[parameter].toPlainText().splitlines()
+                )
+                if value != list(getattr(self.__defaultConfig, parameter)):
+                    configDict[parameter] = value
+
+        return configDict
+
+    def getConfiguration(self, saveToProject=False):
+        """
+        Public method to get the current configuration parameters.
+
+        @param saveToProject flag indicating to save the configuration data in the
+            project file (defaults to False)
+        @type bool (optional)
+        @return dictionary containing the configuration parameters
+        @rtype dict
+        """
+        configuration = self.__getConfigurationDict()
+
+        if saveToProject and self.__project:
+            configuration["config_source"] = self.sourceComboBox.currentData()
+            self.__project.setData("OTHERTOOLSPARMS", "isort", configuration)
+
+        return configuration
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/IsortConfigurationDialog.ui	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,479 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IsortConfigurationDialog</class>
+ <widget class="QDialog" name="IsortConfigurationDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>650</width>
+    <height>700</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>isort Configuration</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_2">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Configuration Source:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QComboBox" name="sourceComboBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="toolTip">
+      <string>Select the configuration source.</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" colspan="2">
+    <widget class="QGroupBox" name="configurationGroup">
+     <property name="title">
+      <string>Configuration</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="0" column="0">
+         <widget class="QLabel" name="label_2">
+          <property name="text">
+           <string>Profile:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <widget class="QComboBox" name="profileComboBox">
+          <property name="toolTip">
+           <string>Enter a profile to be used (empty for none) (see isort documentation).</string>
+          </property>
+          <property name="editable">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="2">
+         <widget class="QLabel" name="label_13">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>&lt;a href=&quot;https://pycqa.github.io/isort/docs/configuration/profiles.html&quot;&gt;Built-In Profiles&lt;/a&gt;</string>
+          </property>
+          <property name="openExternalLinks">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label_3">
+          <property name="text">
+           <string>Target Version:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QComboBox" name="pythonComboBox">
+          <property name="toolTip">
+           <string>Select the target Python version.</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2">
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="2" column="0">
+         <widget class="QLabel" name="label_4">
+          <property name="text">
+           <string>Multi Line Output:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="1">
+         <widget class="QComboBox" name="multiLineComboBox">
+          <property name="toolTip">
+           <string>Select the type of multi line import statements (see isort documentation).</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2">
+         <widget class="QLabel" name="label_12">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>&lt;a href=&quot;https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html&quot;&gt;Defined Multi Line Output Modes&lt;/a&gt;</string>
+          </property>
+          <property name="openExternalLinks">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="0">
+         <widget class="QLabel" name="label_14">
+          <property name="text">
+           <string>Sort Order:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="1">
+         <widget class="QComboBox" name="sortOrderComboBox">
+          <property name="toolTip">
+           <string>Select the sort order (empty for default).</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="2">
+         <widget class="QLabel" name="label_15">
+          <property name="text">
+           <string>&lt;a href=&quot;https://pycqa.github.io/isort/docs/configuration/options.html#sort-order&quot;&gt;Defined Sort Orders&lt;/a&gt;</string>
+          </property>
+          <property name="openExternalLinks">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="0" colspan="3">
+         <widget class="QCheckBox" name="sortCaseSensitiveCheckBox">
+          <property name="toolTip">
+           <string>Select to sort imports observing the case.</string>
+          </property>
+          <property name="text">
+           <string>Sort Case Sensitively</string>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="0">
+         <widget class="QLabel" name="label_16">
+          <property name="text">
+           <string>Supported Extensions:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="1" colspan="2">
+         <widget class="QLineEdit" name="extensionsEdit">
+          <property name="toolTip">
+           <string>Enter the supported extensions separated by space (empty for default).</string>
+          </property>
+          <property name="clearButtonEnabled">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QGridLayout" name="gridLayout">
+        <item row="0" column="0" colspan="2">
+         <widget class="QLabel" name="label_5">
+          <property name="text">
+           <string>Line Length:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="2">
+         <widget class="QSpinBox" name="lineLengthSpinBox">
+          <property name="toolTip">
+           <string>Enter the allowed maximum line length.</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="minimum">
+           <number>70</number>
+          </property>
+          <property name="maximum">
+           <number>120</number>
+          </property>
+          <property name="value">
+           <number>79</number>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="3">
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>55</width>
+            <height>17</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="1" column="0" colspan="2">
+         <widget class="QLabel" name="label_6">
+          <property name="text">
+           <string>Lines Before Imports:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2">
+         <widget class="QSpinBox" name="linesBeforeImportsSpinBox">
+          <property name="toolTip">
+           <string>Enter the number of lines before import statements.</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="specialValueText">
+           <string>automatic</string>
+          </property>
+          <property name="minimum">
+           <number>-1</number>
+          </property>
+          <property name="maximum">
+           <number>5</number>
+          </property>
+          <property name="value">
+           <number>-1</number>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="4" colspan="2">
+         <widget class="QLabel" name="label_7">
+          <property name="text">
+           <string>Lines After Imports:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="6">
+         <widget class="QSpinBox" name="linesAfterImportsSpinBox">
+          <property name="toolTip">
+           <string>Enter the number of lines after import statements.</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="specialValueText">
+           <string>automatic</string>
+          </property>
+          <property name="minimum">
+           <number>-1</number>
+          </property>
+          <property name="maximum">
+           <number>120</number>
+          </property>
+          <property name="value">
+           <number>-1</number>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="7">
+         <spacer name="horizontalSpacer_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>87</width>
+            <height>17</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="2" column="0" colspan="2">
+         <widget class="QLabel" name="label_8">
+          <property name="text">
+           <string>Lines Between Sections:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2">
+         <widget class="QSpinBox" name="linesBetweenSectionsSpinBox">
+          <property name="toolTip">
+           <string>Enter the number of lines between import sections.</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="minimum">
+           <number>0</number>
+          </property>
+          <property name="maximum">
+           <number>5</number>
+          </property>
+          <property name="value">
+           <number>1</number>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="4" colspan="2">
+         <widget class="QLabel" name="label_9">
+          <property name="text">
+           <string>Lines Between Types:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="6">
+         <widget class="QSpinBox" name="linesBetweenTypesSpinBox">
+          <property name="toolTip">
+           <string>Enter the number of lines between import types.</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="minimum">
+           <number>0</number>
+          </property>
+          <property name="maximum">
+           <number>5</number>
+          </property>
+          <property name="value">
+           <number>0</number>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="0" colspan="4">
+         <widget class="QCheckBox" name="trailingCommaCheckBox">
+          <property name="toolTip">
+           <string>Select to include a trailing comma.</string>
+          </property>
+          <property name="text">
+           <string>Include Trailing Comma</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="4" colspan="4">
+         <widget class="QCheckBox" name="parenthesesCheckBox">
+          <property name="toolTip">
+           <string>Select for parenthesized import statements.</string>
+          </property>
+          <property name="text">
+           <string>Use Parentheses</string>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="0">
+         <widget class="QLabel" name="label_10">
+          <property name="text">
+           <string>Sections:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="1" colspan="3">
+         <widget class="QPlainTextEdit" name="sectionsEdit">
+          <property name="toolTip">
+           <string>Enter the order of sections (one per line).</string>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="4">
+         <widget class="QLabel" name="label_11">
+          <property name="text">
+           <string>Exclude:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="5" colspan="3">
+         <widget class="QPlainTextEdit" name="excludeEdit">
+          <property name="toolTip">
+           <string>Enter glob patterns for files to be skipped.</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>sourceComboBox</tabstop>
+  <tabstop>profileComboBox</tabstop>
+  <tabstop>pythonComboBox</tabstop>
+  <tabstop>multiLineComboBox</tabstop>
+  <tabstop>sortOrderComboBox</tabstop>
+  <tabstop>sortCaseSensitiveCheckBox</tabstop>
+  <tabstop>extensionsEdit</tabstop>
+  <tabstop>lineLengthSpinBox</tabstop>
+  <tabstop>linesBeforeImportsSpinBox</tabstop>
+  <tabstop>linesAfterImportsSpinBox</tabstop>
+  <tabstop>linesBetweenSectionsSpinBox</tabstop>
+  <tabstop>linesBetweenTypesSpinBox</tabstop>
+  <tabstop>trailingCommaCheckBox</tabstop>
+  <tabstop>parenthesesCheckBox</tabstop>
+  <tabstop>sectionsEdit</tabstop>
+  <tabstop>excludeEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>IsortConfigurationDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>353</x>
+     <y>453</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>376</x>
+     <y>371</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>IsortConfigurationDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>166</x>
+     <y>452</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>186</x>
+     <y>365</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/IsortFormattingAction.py	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an enum defining the various isort code formatting actions.
+"""
+
+import enum
+
+
+class IsortFormattingAction(enum.Enum):
+    """
+    Class defining the various isort code formatting actions.
+    """
+
+    Sort = 0
+    Check = 1
+    Diff = 2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/IsortFormattingDialog.py	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,577 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog showing the isort code formatting progress and the results.
+"""
+
+import copy
+import io
+import multiprocessing
+import pathlib
+
+from dataclasses import dataclass
+
+from isort.settings import Config
+from isort.api import check_file, sort_file
+
+from PyQt6.QtCore import pyqtSlot, Qt, QCoreApplication
+from PyQt6.QtWidgets import (
+    QAbstractButton,
+    QDialog,
+    QDialogButtonBox,
+    QHeaderView,
+    QTreeWidgetItem,
+)
+
+from eric7 import Preferences
+from eric7.EricWidgets import EricMessageBox
+
+from .FormattingDiffWidget import FormattingDiffWidget
+from .IsortFormattingAction import IsortFormattingAction
+from .IsortUtilities import suppressStderr
+
+from .Ui_IsortFormattingDialog import Ui_IsortFormattingDialog
+
+
+class IsortFormattingDialog(QDialog, Ui_IsortFormattingDialog):
+    """
+    Class implementing a dialog showing the isort code formatting progress and the
+    results.
+    """
+
+    DataTypeRole = Qt.ItemDataRole.UserRole
+    DataRole = Qt.ItemDataRole.UserRole + 1
+
+    StatusColumn = 0
+    FileNameColumn = 1
+
+    def __init__(
+        self,
+        configuration,
+        filesList,
+        project=None,
+        action=IsortFormattingAction.Sort,
+        parent=None,
+    ):
+        """
+        Constructor
+
+        @param configuration dictionary containing the configuration parameters
+        @type dict
+        @param filesList list of absolute file paths to be processed
+        @type list of str
+        @param project reference to the project object (defaults to None)
+        @type Project (optional)
+        @param action action to be performed (defaults to IsortFormattingAction.Sort)
+        @type IsortFormattingAction (optional)
+        @param parent reference to the parent widget (defaults to None)
+        @type QWidget (optional)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+
+        self.progressBar.setMaximum(len(filesList))
+
+        self.resultsList.header().setSortIndicator(1, Qt.SortOrder.AscendingOrder)
+
+        self.__config = copy.deepcopy(configuration)
+        self.__config["quiet"] = True  # we don't want extra output
+        self.__config["overwrite_in_place"] = True  # we want to overwrite the files
+        if "config_source" in self.__config:
+            del self.__config["config_source"]
+        self.__isortConfig = Config(**self.__config)
+        self.__config["__action__"] = action  # needed by the workers
+        self.__project = project
+
+        self.__filesList = filesList[:]
+
+        self.__diffDialog = None
+
+        self.__allFilter = self.tr("<all>")
+
+        self.__sortImportsButton = self.buttonBox.addButton(
+            self.tr("Sort Imports"), QDialogButtonBox.ButtonRole.ActionRole
+        )
+        self.__sortImportsButton.setVisible(False)
+
+        self.show()
+        QCoreApplication.processEvents()
+
+        self.__performAction()
+
+    def __performAction(self):
+        """
+        Private method to execute the requested formatting action.
+        """
+        self.progressBar.setValue(0)
+        self.progressBar.setVisible(True)
+
+        self.statisticsGroup.setVisible(False)
+        self.__statistics = IsortStatistics()
+
+        self.__cancelled = False
+
+        self.statusFilterComboBox.clear()
+        self.resultsList.clear()
+
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(True)
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(False)
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setDefault(True)
+
+        files = self.__filterFiles(self.__filesList)
+        if len(files) > 1:
+            self.__formatManyFiles(files)
+        elif len(files) == 1:
+            self.__formatOneFile(files[0])
+
+    def __filterFiles(self, filesList):
+        """
+        Private method to filter the given list of files according the
+        configuration parameters.
+
+        @param filesList list of files
+        @type list of str
+        @return list of filtered files
+        @rtype list of str
+        """
+        files = []
+        for file in filesList:
+            if not self.__isortConfig.is_supported_filetype(
+                file
+            ) or self.__isortConfig.is_skipped(pathlib.Path(file)):
+                self.__handleIsortResult(file, "skipped")
+            else:
+                files.append(file)
+
+        return files
+
+    def __resort(self):
+        """
+        Private method to resort the result list.
+        """
+        self.resultsList.sortItems(
+            self.resultsList.sortColumn(),
+            self.resultsList.header().sortIndicatorOrder(),
+        )
+
+    def __resizeColumns(self):
+        """
+        Private method to resize the columns of the result list.
+        """
+        self.resultsList.header().resizeSections(
+            QHeaderView.ResizeMode.ResizeToContents
+        )
+        self.resultsList.header().setStretchLastSection(True)
+
+    def __populateStatusFilterCombo(self):
+        """
+        Private method to populate the status filter combo box with allowed selections.
+        """
+        allowedSelections = set()
+        for row in range(self.resultsList.topLevelItemCount()):
+            allowedSelections.add(
+                self.resultsList.topLevelItem(row).text(
+                    IsortFormattingDialog.StatusColumn
+                )
+            )
+
+        self.statusFilterComboBox.addItem(self.__allFilter)
+        self.statusFilterComboBox.addItems(sorted(allowedSelections))
+
+    def __finish(self):
+        """
+        Private method to perform some actions after the run was performed or canceled.
+        """
+        self.__resort()
+        self.__resizeColumns()
+
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True)
+        self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True)
+
+        self.progressBar.setVisible(False)
+
+        self.__sortImportsButton.setVisible(
+            self.__config["__action__"] is not IsortFormattingAction.Sort
+            and self.__statistics.changeCount > 0
+        )
+
+        self.__updateStatistics()
+        self.__populateStatusFilterCombo()
+
+    def __updateStatistics(self):
+        """
+        Private method to update the statistics about the recent formatting run and
+        make them visible.
+        """
+        self.reformattedLabel.setText(
+            self.tr("Reformatted:")
+            if self.__config["__action__"] is IsortFormattingAction.Sort
+            else self.tr("Would Reformat:")
+        )
+
+        total = self.progressBar.maximum()
+
+        self.totalCountLabel.setText("{0:n}".format(total))
+        self.skippedCountLabel.setText("{0:n}".format(self.__statistics.skippedCount))
+        self.failuresCountLabel.setText("{0:n}".format(self.__statistics.failureCount))
+        self.processedCountLabel.setText(
+            "{0:n}".format(self.__statistics.processedCount)
+        )
+        self.reformattedCountLabel.setText(
+            "{0:n}".format(self.__statistics.changeCount)
+        )
+        self.unchangedCountLabel.setText("{0:n}".format(self.__statistics.sameCount))
+
+        self.statisticsGroup.setVisible(True)
+
+    @pyqtSlot(QAbstractButton)
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot to handle button presses of the dialog buttons.
+
+        @param button reference to the pressed button
+        @type QAbstractButton
+        """
+        if button == self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel):
+            self.__cancelled = True
+        elif button == self.buttonBox.button(QDialogButtonBox.StandardButton.Close):
+            self.accept()
+        elif button is self.__sortImportsButton:
+            self.__sortImportsButtonClicked()
+
+    @pyqtSlot()
+    def __sortImportsButtonClicked(self):
+        """
+        Private slot handling the selection of the 'Sort Imports' button.
+        """
+        self.__config["__action__"] = IsortFormattingAction.Sort
+
+        self.__performAction()
+
+    @pyqtSlot(QTreeWidgetItem, int)
+    def on_resultsList_itemDoubleClicked(self, item, column):
+        """
+        Private slot handling a double click of a result item.
+
+        @param item reference to the double clicked item
+        @type QTreeWidgetItem
+        @param column column number that was double clicked
+        @type int
+        """
+        dataType = item.data(0, IsortFormattingDialog.DataTypeRole)
+        if dataType == "error":
+            EricMessageBox.critical(
+                self,
+                self.tr("Formatting Failure"),
+                self.tr("<p>Formatting failed due to this error.</p><p>{0}</p>").format(
+                    item.data(0, IsortFormattingDialog.DataRole)
+                ),
+            )
+        elif dataType == "diff":
+            if self.__diffDialog is None:
+                self.__diffDialog = FormattingDiffWidget()
+            self.__diffDialog.showDiff(item.data(0, IsortFormattingDialog.DataRole))
+
+    @pyqtSlot(str)
+    def on_statusFilterComboBox_currentTextChanged(self, status):
+        """
+        Private slot handling the selection of a status for items to be shown.
+
+        @param status selected status
+        @type str
+        """
+        for row in range(self.resultsList.topLevelItemCount()):
+            itm = self.resultsList.topLevelItem(row)
+            itm.setHidden(
+                status != self.__allFilter
+                and itm.text(IsortFormattingDialog.StatusColumn) != status
+            )
+
+    def closeEvent(self, evt):
+        """
+        Protected slot implementing a close event handler.
+
+        @param evt reference to the close event
+        @type QCloseEvent
+        """
+        if self.__diffDialog is not None:
+            self.__diffDialog.close()
+        evt.accept()
+
+    def __handleIsortResult(self, filename, status, data=""):
+        """
+        Private method to handle an isort formatting result.
+
+        @param filename name of the processed file
+        @type str
+        @param status status of the performed action (one of 'changed', 'failed',
+            'skipped' or 'unchanged')
+        @type str
+        @param data action data (error message or unified diff) (defaults to "")
+        @type str (optional)
+        """
+        isError = False
+
+        if status == "changed":
+            statusMsg = (
+                self.tr("would resort")
+                if self.__config["__action__"]
+                in (IsortFormattingAction.Check, IsortFormattingAction.Diff)
+                else self.tr("resorted")
+            )
+            self.__statistics.changeCount += 1
+
+        elif status == "unchanged":
+            statusMsg = self.tr("unchanged")
+            self.__statistics.sameCount += 1
+
+        elif status == "skipped":
+            statusMsg = self.tr("skipped")
+            self.__statistics.skippedCount += 1
+
+        elif status == "failed":
+            statusMsg = self.tr("failed")
+            self.__statistics.failureCount += 1
+            isError = True
+
+        elif status == "unsupported":
+            statusMsg = self.tr("error")
+            data = self.tr("Unsupported 'isort' action ({0}) given.").format(
+                self.__config["__action__"]
+            )
+            self.__statistics.failureCount += 1
+            isError = True
+
+        else:
+            statusMsg = self.tr("invalid status ({0})").format(status)
+            self.__statistics.failureCount += 1
+            isError = True
+
+        if status != "skipped":
+            self.__statistics.processedCount += 1
+
+        if self.__project:
+            filename = self.__project.getRelativePath(filename)
+
+        itm = QTreeWidgetItem(self.resultsList, [statusMsg, filename])
+        if data:
+            itm.setData(
+                0, IsortFormattingDialog.DataTypeRole, "error" if isError else "diff"
+            )
+            itm.setData(0, IsortFormattingDialog.DataRole, data)
+
+        self.progressBar.setValue(self.progressBar.value() + 1)
+
+        QCoreApplication.processEvents()
+
+    def __formatManyFiles(self, files):
+        """
+        Private method to format the list of files according the configuration using
+        multiple processes in parallel.
+
+        @param files list of files to be processed
+        @type list of str
+        """
+        maxProcesses = Preferences.getUI("BackgroundServiceProcesses")
+        if maxProcesses == 0:
+            # determine based on CPU count
+            try:
+                NumberOfProcesses = multiprocessing.cpu_count()
+                if NumberOfProcesses >= 1:
+                    NumberOfProcesses -= 1
+            except NotImplementedError:
+                NumberOfProcesses = 1
+        else:
+            NumberOfProcesses = maxProcesses
+
+        # Create queues
+        taskQueue = multiprocessing.Queue()
+        doneQueue = multiprocessing.Queue()
+
+        # Submit tasks (initially two times the number of processes)
+        tasks = len(files)
+        initialTasks = min(2 * NumberOfProcesses, tasks)
+        for _ in range(initialTasks):
+            file = files.pop(0)
+            taskQueue.put((file, self.__config["__action__"]))
+
+        # Start worker processes
+        workers = [
+            multiprocessing.Process(
+                target=self.formattingWorkerTask,
+                args=(taskQueue, doneQueue, self.__isortConfig),
+            )
+            for _ in range(NumberOfProcesses)
+        ]
+        for worker in workers:
+            worker.start()
+
+        # Get the results from the worker tasks
+        for _ in range(tasks):
+            result = doneQueue.get()
+            self.__handleIsortResult(result.filename, result.status, data=result.data)
+
+            if self.__cancelled:
+                break
+
+            if files:
+                file = files.pop(0)
+                taskQueue.put((file, self.__config["__action__"]))
+
+        # Tell child processes to stop
+        for _ in range(NumberOfProcesses):
+            taskQueue.put("STOP")
+
+        for worker in workers:
+            worker.join()
+            worker.close()
+
+        taskQueue.close()
+        doneQueue.close()
+
+        self.__finish()
+
+    @staticmethod
+    def formattingWorkerTask(inputQueue, outputQueue, isortConfig):
+        """
+        Static method acting as the parallel worker for the formatting task.
+
+        @param inputQueue input queue
+        @type multiprocessing.Queue
+        @param outputQueue output queue
+        @type multiprocessing.Queue
+        @param isortConfig config object for isort
+        @type isort.Config
+        """
+        for file, action in iter(inputQueue.get, "STOP"):
+            if action == IsortFormattingAction.Diff:
+                result = IsortFormattingDialog.__isortCheckFile(
+                    file,
+                    isortConfig,
+                    withDiff=True,
+                )
+
+            elif action == IsortFormattingAction.Sort:
+                result = IsortFormattingDialog.__isortSortFile(
+                    file,
+                    isortConfig,
+                )
+
+            else:
+                result = IsortResult(
+                    status="unsupported",
+                    filename=file,
+                )
+
+            outputQueue.put(result)
+
+    def __formatOneFile(self, file):
+        """
+        Private method to format the list of files according the configuration.
+
+        @param file name of the file to be processed
+        @type str
+        """
+        if self.__config["__action__"] == IsortFormattingAction.Diff:
+            result = IsortFormattingDialog.__isortCheckFile(
+                file,
+                self.__isortConfig,
+                withDiff=True,
+            )
+
+        elif self.__config["__action__"] == IsortFormattingAction.Sort:
+            result = IsortFormattingDialog.__isortSortFile(
+                file,
+                self.__isortConfig,
+            )
+
+        else:
+            result = IsortResult(
+                status="unsupported",
+                filename=file,
+            )
+
+        self.__handleIsortResult(result.filename, result.status, data=result.data)
+
+        self.__finish()
+
+    @staticmethod
+    def __isortCheckFile(filename, isortConfig, withDiff=True):
+        """
+        Static method to check, if a file's import statements need to be changed.
+
+        @param filename name of the file to be processed
+        @type str
+        @param isortConfig config object for isort
+        @type isort.Config
+        @param withDiff flag indicating to return a unified diff, if the file needs to
+            be changed (defaults to True)
+        @type bool (optional)
+        @return result object
+        @rtype IsortResult
+        """
+        diffIO = io.StringIO() if withDiff else False
+        with suppressStderr():
+            ok = check_file(filename, show_diff=diffIO, config=isortConfig)
+        if withDiff:
+            data = "" if ok else diffIO.getvalue()
+            diffIO.close()
+        else:
+            data = ""
+
+        status = "unchanged" if ok else "changed"
+
+        return IsortResult(status=status, filename=filename, data=data)
+
+    @staticmethod
+    def __isortSortFile(filename, isortConfig):
+        """
+        Static method to sort the import statements of a file.
+
+        @param filename name of the file to be processed
+        @type str
+        @param isortConfig config object for isort
+        @type isort.Config
+        @return result object
+        @rtype IsortResult
+        """
+        with suppressStderr():
+            ok = sort_file(
+                filename,
+                config=isortConfig,
+                ask_to_apply=False,
+                write_to_stdout=False,
+                show_diff=False,
+            )
+
+        status = "changed" if ok else "unchanged"
+
+        return IsortResult(status=status, filename=filename)
+
+
+@dataclass
+class IsortStatistics:
+    """
+    Class containing the isort reformatting statistic data.
+    """
+
+    skippedCount: int = 0
+    changeCount: int = 0
+    sameCount: int = 0
+    failureCount: int = 0
+    processedCount: int = 0
+
+
+@dataclass
+class IsortResult:
+    """
+    Class containing the isort reformatting result data.
+    """
+
+    status: str = ""
+    filename: str = ""
+    data: str = ""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/IsortFormattingDialog.ui	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>IsortFormattingDialog</class>
+ <widget class="QDialog" name="IsortFormattingDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>700</width>
+    <height>650</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Formatting Imports with isort</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer_3">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Status Filter:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="statusFilterComboBox">
+       <property name="minimumSize">
+        <size>
+         <width>150</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="toolTip">
+        <string>Select the status of items to be shown (empty for all).</string>
+       </property>
+       <property name="sizeAdjustPolicy">
+        <enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTreeWidget" name="resultsList">
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="rootIsDecorated">
+      <bool>false</bool>
+     </property>
+     <property name="itemsExpandable">
+      <bool>false</bool>
+     </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+     <column>
+      <property name="text">
+       <string>Status</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>File Name</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="statisticsGroup">
+     <property name="title">
+      <string>Statistics</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Total Files:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="totalCountLabel">
+        <property name="text">
+         <string notr="true">0</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>183</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="0" column="3">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>Skipped</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="4">
+       <widget class="QLabel" name="skippedCountLabel">
+        <property name="text">
+         <string notr="true">0</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="5">
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>182</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="0" column="6">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Failures:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="7">
+       <widget class="QLabel" name="failuresCountLabel">
+        <property name="text">
+         <string notr="true">0</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Processed:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="processedCountLabel">
+        <property name="text">
+         <string notr="true">0</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="3">
+       <widget class="QLabel" name="reformattedLabel">
+        <property name="text">
+         <string>Resorted</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="4">
+       <widget class="QLabel" name="reformattedCountLabel">
+        <property name="text">
+         <string notr="true">0</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="6">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Unchanged:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="7">
+       <widget class="QLabel" name="unchangedCountLabel">
+        <property name="text">
+         <string notr="true">0</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QProgressBar" name="progressBar">
+     <property name="format">
+      <string>%v/%m Files</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>resultsList</tabstop>
+  <tabstop>statusFilterComboBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/CodeFormatting/IsortUtilities.py	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing some utility functions for the isort import statement formatting
+tool.
+"""
+
+import contextlib
+import os
+import sys
+
+import isort
+
+from PyQt6.QtCore import QCoreApplication, pyqtSlot
+
+from eric7.EricWidgets import EricMessageBox
+
+
+@pyqtSlot()
+def aboutIsort():
+    """
+    Slot to show an 'About isort' dialog.
+    """
+    EricMessageBox.information(
+        None,
+        QCoreApplication.translate("IsortUtilities", "About isort"),
+        QCoreApplication.translate(
+            "IsortUtilities",
+            """<p><b>isort Version {0}</b></p>"""
+            """<p><i>isort</i> is a Python utility / library to sort imports"""
+            """ alphabetically, and automatically separated into sections and by"""
+            """ type.</p>""",
+        ).format(isort.__version__),
+    )
+
+
+@contextlib.contextmanager
+def suppressStderr():
+    """
+    Function to suppress output to stderr.
+    """
+    stderr = sys.stderr
+    with open(os.devnull, "w") as devnull:
+        sys.stderr = devnull
+        yield
+        sys.stderr = stderr
--- a/src/eric7/CodeFormatting/__init__.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/CodeFormatting/__init__.py	Mon Oct 31 15:29:18 2022 +0100
@@ -9,5 +9,6 @@
 The supported code formatting systems are these.
 <ul>
 <li><a href="https://github.com/psf/black">Black</a></li>
+<li><a href="https://pycqa.github.io/isort/">isort</a></li>
 </ul
 """
--- a/src/eric7/Documentation/Source/eric7.CodeFormatting.BlackDiffWidget.html	Mon Oct 31 14:09:07 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-<!DOCTYPE html>
-<html><head>
-<title>eric7.CodeFormatting.BlackDiffWidget</title>
-<meta charset="UTF-8">
-<link rel="stylesheet" href="styles.css">
-</head>
-<body>
-<a NAME="top" ID="top"></a>
-<h1>eric7.CodeFormatting.BlackDiffWidget</h1>
-
-<p>
-Module implementing a window to show a unified diff..
-</p>
-<h3>Global Attributes</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Classes</h3>
-
-<table>
-
-<tr>
-<td><a href="#BlackDiffWidget">BlackDiffWidget</a></td>
-<td>Class implementing a window to show a unified diff..</td>
-</tr>
-</table>
-<h3>Functions</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<hr />
-<hr />
-<a NAME="BlackDiffWidget" ID="BlackDiffWidget"></a>
-<h2>BlackDiffWidget</h2>
-
-<p>
-    Class implementing a window to show a unified diff..
-</p>
-<h3>Derived from</h3>
-QWidget, Ui_BlackDiffWidget
-<h3>Class Attributes</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Class Methods</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-<h3>Methods</h3>
-
-<table>
-
-<tr>
-<td><a href="#BlackDiffWidget.__init__">BlackDiffWidget</a></td>
-<td>Constructor</td>
-</tr>
-<tr>
-<td><a href="#BlackDiffWidget.closeEvent">closeEvent</a></td>
-<td>Protected slot implementing a close event handler.</td>
-</tr>
-<tr>
-<td><a href="#BlackDiffWidget.showDiff">showDiff</a></td>
-<td>Public method to show the given diff.</td>
-</tr>
-</table>
-<h3>Static Methods</h3>
-
-<table>
-<tr><td>None</td></tr>
-</table>
-
-<a NAME="BlackDiffWidget.__init__" ID="BlackDiffWidget.__init__"></a>
-<h4>BlackDiffWidget (Constructor)</h4>
-<b>BlackDiffWidget</b>(<i>parent=None</i>)
-
-<p>
-        Constructor
-</p>
-<dl>
-
-<dt><i>parent</i> (QWidget (optional))</dt>
-<dd>
-reference to the parent widget (defaults to None)
-</dd>
-</dl>
-<a NAME="BlackDiffWidget.closeEvent" ID="BlackDiffWidget.closeEvent"></a>
-<h4>BlackDiffWidget.closeEvent</h4>
-<b>closeEvent</b>(<i>evt</i>)
-
-<p>
-        Protected slot implementing a close event handler.
-</p>
-<dl>
-
-<dt><i>evt</i> (QCloseEvent)</dt>
-<dd>
-reference to the close event
-</dd>
-</dl>
-<a NAME="BlackDiffWidget.showDiff" ID="BlackDiffWidget.showDiff"></a>
-<h4>BlackDiffWidget.showDiff</h4>
-<b>showDiff</b>(<i>diff</i>)
-
-<p>
-        Public method to show the given diff.
-</p>
-<dl>
-
-<dt><i>diff</i> (str)</dt>
-<dd>
-text containing the unified diff
-</dd>
-</dl>
-<div align="right"><a href="#top">Up</a></div>
-<hr />
-</body></html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/Documentation/Source/eric7.CodeFormatting.FormattingDiffWidget.html	Mon Oct 31 15:29:18 2022 +0100
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html><head>
+<title>eric7.CodeFormatting.BlackDiffWidget</title>
+<meta charset="UTF-8">
+<link rel="stylesheet" href="styles.css">
+</head>
+<body>
+<a NAME="top" ID="top"></a>
+<h1>eric7.CodeFormatting.BlackDiffWidget</h1>
+
+<p>
+Module implementing a window to show a unified diff..
+</p>
+<h3>Global Attributes</h3>
+
+<table>
+<tr><td>None</td></tr>
+</table>
+<h3>Classes</h3>
+
+<table>
+
+<tr>
+<td><a href="#BlackDiffWidget">BlackDiffWidget</a></td>
+<td>Class implementing a window to show a unified diff..</td>
+</tr>
+</table>
+<h3>Functions</h3>
+
+<table>
+<tr><td>None</td></tr>
+</table>
+<hr />
+<hr />
+<a NAME="BlackDiffWidget" ID="BlackDiffWidget"></a>
+<h2>BlackDiffWidget</h2>
+
+<p>
+    Class implementing a window to show a unified diff..
+</p>
+<h3>Derived from</h3>
+QWidget, Ui_BlackDiffWidget
+<h3>Class Attributes</h3>
+
+<table>
+<tr><td>None</td></tr>
+</table>
+<h3>Class Methods</h3>
+
+<table>
+<tr><td>None</td></tr>
+</table>
+<h3>Methods</h3>
+
+<table>
+
+<tr>
+<td><a href="#BlackDiffWidget.__init__">BlackDiffWidget</a></td>
+<td>Constructor</td>
+</tr>
+<tr>
+<td><a href="#BlackDiffWidget.closeEvent">closeEvent</a></td>
+<td>Protected slot implementing a close event handler.</td>
+</tr>
+<tr>
+<td><a href="#BlackDiffWidget.showDiff">showDiff</a></td>
+<td>Public method to show the given diff.</td>
+</tr>
+</table>
+<h3>Static Methods</h3>
+
+<table>
+<tr><td>None</td></tr>
+</table>
+
+<a NAME="BlackDiffWidget.__init__" ID="BlackDiffWidget.__init__"></a>
+<h4>BlackDiffWidget (Constructor)</h4>
+<b>BlackDiffWidget</b>(<i>parent=None</i>)
+
+<p>
+        Constructor
+</p>
+<dl>
+
+<dt><i>parent</i> (QWidget (optional))</dt>
+<dd>
+reference to the parent widget (defaults to None)
+</dd>
+</dl>
+<a NAME="BlackDiffWidget.closeEvent" ID="BlackDiffWidget.closeEvent"></a>
+<h4>BlackDiffWidget.closeEvent</h4>
+<b>closeEvent</b>(<i>evt</i>)
+
+<p>
+        Protected slot implementing a close event handler.
+</p>
+<dl>
+
+<dt><i>evt</i> (QCloseEvent)</dt>
+<dd>
+reference to the close event
+</dd>
+</dl>
+<a NAME="BlackDiffWidget.showDiff" ID="BlackDiffWidget.showDiff"></a>
+<h4>BlackDiffWidget.showDiff</h4>
+<b>showDiff</b>(<i>diff</i>)
+
+<p>
+        Public method to show the given diff.
+</p>
+<dl>
+
+<dt><i>diff</i> (str)</dt>
+<dd>
+text containing the unified diff
+</dd>
+</dl>
+<div align="right"><a href="#top">Up</a></div>
+<hr />
+</body></html>
\ No newline at end of file
--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py	Mon Oct 31 15:29:18 2022 +0100
@@ -396,6 +396,8 @@
     ## adapted from: flake8-alphabetize v0.0.17
     #######################################################################
 
+    # TODO: upgrade to v0.0.18
+    # TODO: introduce sort type iaw. isort
     def __checkImportOrder(self):
         """
         Private method to check the order of import statements.
--- a/src/eric7/Project/Project.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/Project/Project.py	Mon Oct 31 15:29:18 2022 +0100
@@ -59,6 +59,9 @@
 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction
 from eric7.CodeFormatting.BlackUtilities import aboutBlack
 
+from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction
+from eric7.CodeFormatting.IsortUtilities import aboutIsort
+
 
 class Project(QObject):
     """
@@ -4867,7 +4870,7 @@
         self.actions.append(self.createSBOMAct)
 
         ###################################################################
-        ## Project Tools - code formatting actions
+        ## Project Tools - code formatting actions - Black
         ###################################################################
 
         self.blackFormattingGrp = createActionGroup(self)
@@ -4993,6 +4996,108 @@
         self.actions.append(self.blackConfigureAct)
 
         ###################################################################
+        ## Project Tools - code formatting actions - isort
+        ###################################################################
+
+        self.isortFormattingGrp = createActionGroup(self)
+
+        self.isortAboutAct = EricAction(
+            self.tr("About isort"),
+            self.tr("&isort"),
+            0,
+            0,
+            self.isortFormattingGrp,
+            "project_isort_about",
+        )
+        self.isortAboutAct.setStatusTip(self.tr("Show some information about 'isort'."))
+        self.isortAboutAct.setWhatsThis(
+            self.tr(
+                "<b>isort</b>"
+                "<p>This shows some information about the installed 'isort' tool.</p>"
+            )
+        )
+        self.isortAboutAct.triggered.connect(aboutIsort)
+        self.actions.append(self.isortAboutAct)
+        font = self.isortAboutAct.font()
+        font.setBold(True)
+        self.isortAboutAct.setFont(font)
+
+        self.isortSortImportsAct = EricAction(
+            self.tr("Sort Imports"),
+            self.tr("Sort Imports"),
+            0,
+            0,
+            self.isortFormattingGrp,
+            "project_isort_sort_imports",
+        )
+        self.isortSortImportsAct.setStatusTip(
+            self.tr("Sort the import statements of the project sources with 'isort'.")
+        )
+        self.isortSortImportsAct.setWhatsThis(
+            self.tr(
+                "<b>Sort Imports</b>"
+                "<p>This shows a dialog to enter parameters for the imports sorting"
+                " run and sorts the import statements of the project sources using"
+                " 'isort'.</p>"
+            )
+        )
+        self.isortSortImportsAct.triggered.connect(
+            lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Sort)
+        )
+        self.actions.append(self.isortSortImportsAct)
+
+        self.isortDiffSortingAct = EricAction(
+            self.tr("Imports Sorting Diff"),
+            self.tr("Imports Sorting Diff"),
+            0,
+            0,
+            self.isortFormattingGrp,
+            "project_isort_diff_code",
+        )
+        self.isortDiffSortingAct.setStatusTip(
+            self.tr(
+                "Generate a unified diff of potential project source imports"
+                " resorting with 'isort'."
+            )
+        )
+        self.isortDiffSortingAct.setWhatsThis(
+            self.tr(
+                "<b>Imports Sorting Diff</b>"
+                "<p>This shows a dialog to enter parameters for the imports sorting"
+                " diff run and generates a unified diff of potential project source"
+                " changes using 'isort'.</p>"
+            )
+        )
+        self.isortDiffSortingAct.triggered.connect(
+            lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Diff)
+        )
+        self.actions.append(self.isortDiffSortingAct)
+
+        self.isortConfigureAct = EricAction(
+            self.tr("Configure"),
+            self.tr("Configure"),
+            0,
+            0,
+            self.isortFormattingGrp,
+            "project_isort_configure",
+        )
+        self.isortConfigureAct.setStatusTip(
+            self.tr(
+                "Enter the parameters for resorting the project sources import"
+                " statements with 'isort'."
+            )
+        )
+        self.isortConfigureAct.setWhatsThis(
+            self.tr(
+                "<b>Configure</b>"
+                "<p>This shows a dialog to enter the parameters for resorting the"
+                " import statements of the project sources with 'isort'.</p>"
+            )
+        )
+        self.isortConfigureAct.triggered.connect(self.__configureIsort)
+        self.actions.append(self.isortConfigureAct)
+
+        ###################################################################
         ## Project - embedded environment actions
         ###################################################################
 
@@ -5205,6 +5310,8 @@
         self.formattingMenu.setTearOffEnabled(True)
         self.formattingMenu.addActions(self.blackFormattingGrp.actions())
         self.formattingMenu.addSeparator()
+        self.formattingMenu.addActions(self.isortFormattingGrp.actions())
+        self.formattingMenu.addSeparator()
 
         # build the project main menu
         menu.setTearOffEnabled(True)
@@ -6787,6 +6894,56 @@
             dlg.getConfiguration(saveToProject=True)
             # The data is saved to the project as a side effect.
 
+    def __performImportSortingWithIsort(self, action):
+        """
+        Private method to format the project sources import statements using the
+        'isort' tool.
+
+        Following actions are supported.
+        <ul>
+        <li>IsortFormattingAction.Format - the imports reformatting is performed</li>
+        <li>IsortFormattingAction.Check - a check is performed, if imports formatting
+            is necessary</li>
+        <li>IsortFormattingAction.Diff - a unified diff of potential imports formatting
+            changes is generated</li>
+        </ul>
+
+        @param action formatting operation to be performed
+        @type IsortFormattingAction
+        """
+        from eric7.CodeFormatting.IsortConfigurationDialog import (
+            IsortConfigurationDialog,
+        )
+        from eric7.CodeFormatting.IsortFormattingDialog import IsortFormattingDialog
+
+        if ericApp().getObject("ViewManager").checkAllDirty():
+            dlg = IsortConfigurationDialog(withProject=True)
+            if dlg.exec() == QDialog.DialogCode.Accepted:
+                config = dlg.getConfiguration(saveToProject=True)
+
+                isortDialog = IsortFormattingDialog(
+                    config,
+                    self.getProjectFiles("SOURCES", normalized=True),
+                    project=self,
+                    action=action,
+                )
+                isortDialog.exec()
+
+    @pyqtSlot()
+    def __configureIsort(self):
+        """
+        Private slot to enter the parameters for formatting the import statements of the
+        project sources with 'isort'.
+        """
+        from eric7.CodeFormatting.IsortConfigurationDialog import (
+            IsortConfigurationDialog,
+        )
+
+        dlg = IsortConfigurationDialog(withProject=True, onlyProject=True)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            dlg.getConfiguration(saveToProject=True)
+            # The data is saved to the project as a side effect.
+
     #########################################################################
     ## Below are methods implementing the 'Embedded Environment' support
     #########################################################################
--- a/src/eric7/Project/ProjectSourcesBrowser.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/Project/ProjectSourcesBrowser.py	Mon Oct 31 15:29:18 2022 +0100
@@ -38,6 +38,9 @@
 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction
 from eric7.CodeFormatting.BlackUtilities import aboutBlack
 
+from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction
+from eric7.CodeFormatting.IsortUtilities import aboutIsort
+
 
 class ProjectSourcesBrowser(ProjectBaseBrowser):
     """
@@ -147,6 +150,20 @@
             self.tr("Formatting Diff"),
             lambda: self.__performFormatWithBlack(BlackFormattingAction.Diff),
         )
+        self.formattingMenu.addSeparator()
+        act = self.formattingMenu.addAction(self.tr("isort"), aboutIsort)
+        font = act.font()
+        font.setBold(True)
+        act.setFont(font)
+        self.formattingMenu.addAction(
+            self.tr("Sort Imports"),
+            lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Sort),
+        )
+        self.formattingMenu.addAction(
+            self.tr("Imports Sorting Diff"),
+            lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Diff),
+        )
+        self.formattingMenu.addSeparator()
         self.formattingMenu.aboutToShow.connect(self.__showContextMenuFormatting)
 
         self.menuShow = QMenu(self.tr("Show"))
@@ -1271,15 +1288,7 @@
 
         files = [
             itm.fileName()
-            for itm in self.getSelectedItems(
-                [
-                    BrowserFileItem,
-                    BrowserClassItem,
-                    BrowserMethodItem,
-                    BrowserClassAttributeItem,
-                    BrowserImportItem,
-                ]
-            )
+            for itm in self.getSelectedItems([BrowserFileItem])
             if itm.isPython3File()
         ]
         if not files:
@@ -1310,3 +1319,59 @@
                 self.tr("Code Formatting"),
                 self.tr("""There are no files left for reformatting."""),
             )
+
+    def __performImportSortingWithIsort(self, action):
+        """
+        Private method to sort the import statements of the selected project sources
+        using the 'isort' tool.
+
+        Following actions are supported.
+        <ul>
+        <li>IsortFormattingAction.Sort - the import statement sorting is performed</li>
+        <li>IsortFormattingAction.Check - a check is performed, if import statement
+            resorting is necessary</li>
+        <li>IsortFormattingAction.Diff - a unified diff of potential import statement
+            changes is generated</li>
+        </ul>
+
+        @param action sorting operation to be performed
+        @type IsortFormattingAction
+        """
+        from eric7.CodeFormatting.IsortConfigurationDialog import (
+            IsortConfigurationDialog,
+        )
+        from eric7.CodeFormatting.IsortFormattingDialog import IsortFormattingDialog
+
+        files = [
+            itm.fileName()
+            for itm in self.getSelectedItems([BrowserFileItem])
+            if itm.isPython3File()
+        ]
+        if not files:
+            # called for a directory
+            itm = self.model().item(self.currentIndex())
+            dirName = itm.dirName()
+            files = [
+                f
+                for f in self.project.getProjectFiles("SOURCES", normalized=True)
+                if f.startswith(dirName)
+            ]
+
+        vm = ericApp().getObject("ViewManager")
+        files = [fn for fn in files if vm.checkFileDirty(fn)]
+
+        if files:
+            dlg = IsortConfigurationDialog(withProject=True)
+            if dlg.exec() == QDialog.DialogCode.Accepted:
+                config = dlg.getConfiguration()
+
+                formattingDialog = IsortFormattingDialog(
+                    config, files, project=self.project, action=action
+                )
+                formattingDialog.exec()
+        else:
+            EricMessageBox.information(
+                self,
+                self.tr("Import Sorting"),
+                self.tr("""There are no files left for import statement sorting."""),
+            )
--- a/src/eric7/QScintilla/DocstringGenerator/PyDocstringGenerator.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/QScintilla/DocstringGenerator/PyDocstringGenerator.py	Mon Oct 31 15:29:18 2022 +0100
@@ -216,9 +216,7 @@
                 docstringList.append(self.__quote3)
                 for index, line in enumerate(docstringList):
                     docstringList[index] = (
-                        indentation + line
-                        if bool(line.strip())
-                        else ""
+                        indentation + line if bool(line.strip()) else ""
                     )
                 return sep.join(docstringList) + sep, (insertLine, 0), newCursorLine
 
@@ -324,9 +322,7 @@
                     docstringList.append(self.__quote3)
                 for index, line in enumerate(docstringList):
                     docstringList[index] = (
-                        indentation + line
-                        if bool(line.strip())
-                        else ""
+                        indentation + line if bool(line.strip()) else ""
                     )
                 docstring = sep.join(docstringList) + indentation
                 return docstring, cursorPosition, newCursorLine
--- a/src/eric7/QScintilla/Editor.py	Mon Oct 31 14:09:07 2022 +0100
+++ b/src/eric7/QScintilla/Editor.py	Mon Oct 31 15:29:18 2022 +0100
@@ -57,6 +57,9 @@
 from eric7.CodeFormatting.BlackFormattingAction import BlackFormattingAction
 from eric7.CodeFormatting.BlackUtilities import aboutBlack
 
+from eric7.CodeFormatting.IsortFormattingAction import IsortFormattingAction
+from eric7.CodeFormatting.IsortUtilities import aboutIsort
+
 EditorAutoCompletionListID = 1
 TemplateCompletionListID = 2
 ReferencesListID = 3
@@ -1046,6 +1049,10 @@
         """
         menu = QMenu(self.tr("Code Formatting"))
 
+        #######################################################################
+        ## Black related entries
+        #######################################################################
+
         act = menu.addAction(self.tr("Black"), aboutBlack)
         font = act.font()
         font.setBold(True)
@@ -1062,6 +1069,25 @@
             self.tr("Formatting Diff"),
             lambda: self.__performFormatWithBlack(BlackFormattingAction.Diff),
         )
+        menu.addSeparator()
+
+        #######################################################################
+        ## isort related entries
+        #######################################################################
+
+        act = menu.addAction(self.tr("isort"), aboutIsort)
+        font = act.font()
+        font.setBold(True)
+        act.setFont(font)
+        menu.addAction(
+            self.tr("Sort Imports"),
+            lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Sort),
+        )
+        menu.addAction(
+            self.tr("Imports Sorting Diff"),
+            lambda: self.__performImportSortingWithIsort(IsortFormattingAction.Diff),
+        )
+        menu.addSeparator()
 
         menu.aboutToShow.connect(self.__showContextMenuFormatting)
 
@@ -9061,7 +9087,7 @@
             self.__cancelMouseHoverHelp()
 
     #######################################################################
-    ## Methods implementing the Black code formatting interface
+    ## Methods implementing the code formatting interface
     #######################################################################
 
     def __performFormatWithBlack(self, action):
@@ -9099,3 +9125,39 @@
                     config, [self.fileName], project=self.project, action=action
                 )
                 formattingDialog.exec()
+
+    def __performImportSortingWithIsort(self, action):
+        """
+        Private method to sort the import statements using the 'isort' tool.
+
+        Following actions are supported.
+        <ul>
+        <li>IsortFormattingAction.Sort - the import statement sorting is performed</li>
+        <li>IsortFormattingAction.Check - a check is performed, if import statement
+            resorting is necessary</li>
+        <li>IsortFormattingAction.Diff - a unified diff of potential import statement
+            changes is generated</li>
+        </ul>
+
+        @param action sorting operation to be performed
+        @type IsortFormattingAction
+        """
+        from eric7.CodeFormatting.IsortConfigurationDialog import (
+            IsortConfigurationDialog,
+        )
+        from eric7.CodeFormatting.IsortFormattingDialog import IsortFormattingDialog
+
+        if not self.isModified() or self.saveFile():
+            withProject = (
+                self.fileName
+                and self.project.isOpen()
+                and self.project.isProjectSource(self.fileName)
+            )
+            dlg = IsortConfigurationDialog(withProject=withProject)
+            if dlg.exec() == QDialog.DialogCode.Accepted:
+                config = dlg.getConfiguration()
+
+                formattingDialog = IsortFormattingDialog(
+                    config, [self.fileName], project=self.project, action=action
+                )
+                formattingDialog.exec()

eric ide

mercurial