diff --git a/python/console/console.py b/python/console/console.py
index 0ee5451f188..24d1ac1c939 100644
--- a/python/console/console.py
+++ b/python/console/console.py
@@ -110,7 +110,6 @@ class PythonConsole(QgsDockWidget):
super().__init__(parent)
self.setObjectName("PythonConsole")
self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console"))
- # self.setAllowedAreas(Qt.BottomDockWidgetArea)
self.console = PythonConsoleWidget(self)
QgsGui.instance().optionsChanged.connect(self.console.updateSettings)
@@ -167,13 +166,9 @@ class PythonConsoleWidget(QWidget):
self.splitter.addWidget(self.shellOutWidget)
self.splitter.addWidget(self.shell)
- # self.splitterEditor.addWidget(self.tabEditorWidget)
-
self.splitterObj = QSplitter(self.splitterEditor)
self.splitterObj.setHandleWidth(3)
self.splitterObj.setOrientation(Qt.Horizontal)
- # self.splitterObj.setSizes([0, 0])
- # self.splitterObj.setStretchFactor(0, 1)
self.widgetEditor = QWidget(self.splitterObj)
self.widgetFind = QWidget(self)
@@ -185,10 +180,6 @@ class PythonConsoleWidget(QWidget):
self.listClassMethod.setColumnHidden(1, True)
self.listClassMethod.setAlternatingRowColors(True)
- # self.splitterEditor.addWidget(self.widgetEditor)
- # self.splitterObj.addWidget(self.listClassMethod)
- # self.splitterObj.addWidget(self.widgetEditor)
-
# Hide side editor on start up
self.splitterObj.hide()
self.listClassMethod.hide()
@@ -286,26 +277,18 @@ class PythonConsoleWidget(QWidget):
self.runScriptEditorButton.setIconVisibleInMenu(True)
self.runScriptEditorButton.setToolTip(runScriptEditorBt)
self.runScriptEditorButton.setText(runScriptEditorBt)
- # Action Run Script (subprocess)
- commentEditorBt = QCoreApplication.translate("PythonConsole", "Comment")
- self.commentEditorButton = QAction(self)
- self.commentEditorButton.setCheckable(False)
- self.commentEditorButton.setEnabled(True)
- self.commentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg"))
- self.commentEditorButton.setMenuRole(QAction.PreferencesRole)
- self.commentEditorButton.setIconVisibleInMenu(True)
- self.commentEditorButton.setToolTip(commentEditorBt)
- self.commentEditorButton.setText(commentEditorBt)
- # Action Run Script (subprocess)
- uncommentEditorBt = QCoreApplication.translate("PythonConsole", "Uncomment")
- self.uncommentEditorButton = QAction(self)
- self.uncommentEditorButton.setCheckable(False)
- self.uncommentEditorButton.setEnabled(True)
- self.uncommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.svg"))
- self.uncommentEditorButton.setMenuRole(QAction.PreferencesRole)
- self.uncommentEditorButton.setIconVisibleInMenu(True)
- self.uncommentEditorButton.setToolTip(uncommentEditorBt)
- self.uncommentEditorButton.setText(uncommentEditorBt)
+
+ # Action Toggle comment
+ toggleText = QCoreApplication.translate("PythonConsole", "Toggle Comment")
+ self.toggleCommentEditorButton = QAction(self)
+ self.toggleCommentEditorButton.setCheckable(False)
+ self.toggleCommentEditorButton.setEnabled(True)
+ self.toggleCommentEditorButton.setIcon(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg"))
+ self.toggleCommentEditorButton.setMenuRole(QAction.PreferencesRole)
+ self.toggleCommentEditorButton.setIconVisibleInMenu(True)
+ self.toggleCommentEditorButton.setToolTip(toggleText + " Ctrl+:")
+ self.toggleCommentEditorButton.setText(toggleText)
+
# Action for Object browser
objList = QCoreApplication.translate("PythonConsole", "Object Inspector…")
self.objectListButton = QAction(self)
@@ -433,8 +416,7 @@ class PythonConsoleWidget(QWidget):
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.findTextButton)
self.toolBarEditor.addSeparator()
- self.toolBarEditor.addAction(self.commentEditorButton)
- self.toolBarEditor.addAction(self.uncommentEditorButton)
+ self.toolBarEditor.addAction(self.toggleCommentEditorButton)
self.toolBarEditor.addSeparator()
self.toolBarEditor.addAction(self.objectListButton)
@@ -527,8 +509,7 @@ class PythonConsoleWidget(QWidget):
self.findTextButton.triggered.connect(self._toggleFind)
self.objectListButton.toggled.connect(self.toggleObjectListWidget)
- self.commentEditorButton.triggered.connect(self.commentCode)
- self.uncommentEditorButton.triggered.connect(self.uncommentCode)
+ self.toggleCommentEditorButton.triggered.connect(self.toggleComment)
self.runScriptEditorButton.triggered.connect(self.runScriptEditor)
self.cutEditorButton.triggered.connect(self.cutEditor)
self.copyEditorButton.triggered.connect(self.copyEditor)
@@ -657,11 +638,8 @@ class PythonConsoleWidget(QWidget):
def runScriptEditor(self):
self.tabEditorWidget.currentWidget().newEditor.runScriptCode()
- def commentCode(self):
- self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(True)
-
- def uncommentCode(self):
- self.tabEditorWidget.currentWidget().newEditor.commentEditorCode(False)
+ def toggleComment(self):
+ self.tabEditorWidget.currentWidget().newEditor.toggleComment()
def openScriptFileExtEditor(self):
tabWidget = self.tabEditorWidget.currentWidget()
diff --git a/python/console/console_editor.py b/python/console/console_editor.py
index 96d0982512b..b23137ce0f5 100644
--- a/python/console/console_editor.py
+++ b/python/console/console_editor.py
@@ -122,12 +122,6 @@ class Editor(QgsCodeEditorPython):
self.syntaxCheckScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_4), self)
self.syntaxCheckScut.setContext(Qt.WidgetShortcut)
self.syntaxCheckScut.activated.connect(self.syntaxCheck)
- self.commentScut = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_3), self)
- self.commentScut.setContext(Qt.WidgetShortcut)
- self.commentScut.activated.connect(self.parent.pc.commentCode)
- self.uncommentScut = QShortcut(QKeySequence(Qt.SHIFT + Qt.CTRL + Qt.Key_3), self)
- self.uncommentScut.setContext(Qt.WidgetShortcut)
- self.uncommentScut.activated.connect(self.parent.pc.uncommentCode)
self.modificationChanged.connect(self.parent.modified)
self.modificationAttempted.connect(self.fileReadOnly)
@@ -178,11 +172,8 @@ class Editor(QgsCodeEditorPython):
self.selectAll, QKeySequence.SelectAll)
menu.addSeparator()
menu.addAction(QgsApplication.getThemeIcon("console/iconCommentEditorConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Comment"),
- self.parent.pc.commentCode, 'Ctrl+3')
- menu.addAction(QgsApplication.getThemeIcon("console/iconUncommentEditorConsole.svg"),
- QCoreApplication.translate("PythonConsole", "Uncomment"),
- self.parent.pc.uncommentCode, 'Shift+Ctrl+3')
+ QCoreApplication.translate("PythonConsole", "Toggle Comment"),
+ self.toggleComment, 'Ctrl+:')
menu.addSeparator()
gist_menu = QMenu(self)
gist_menu.setTitle(QCoreApplication.translate("PythonConsole", "Share on GitHub"))
@@ -338,31 +329,6 @@ class Editor(QgsCodeEditorPython):
else:
self.openFindWidget()
- def commentEditorCode(self, commentCheck):
- self.beginUndoAction()
- if self.hasSelectedText():
- startLine, _, endLine, _ = self.getSelection()
- for line in range(startLine, endLine + 1):
- if commentCheck:
- self.insertAt('#', line, 0)
- else:
- if not self.text(line).strip().startswith('#'):
- continue
- self.setSelection(line, self.indentation(line),
- line, self.indentation(line) + 1)
- self.removeSelectedText()
- else:
- line, pos = self.getCursorPosition()
- if commentCheck:
- self.insertAt('#', line, 0)
- else:
- if not self.text(line).strip().startswith('#'):
- return
- self.setSelection(line, self.indentation(line),
- line, self.indentation(line) + 1)
- self.removeSelectedText()
- self.endUndoAction()
-
def createTempFile(self):
import tempfile
fd, path = tempfile.mkstemp()
@@ -547,7 +513,7 @@ class Editor(QgsCodeEditorPython):
if re.match(ptrn, txt):
self.insert(' import')
self.setCursorPosition(line, pos + 7)
- QsciScintilla.keyPressEvent(self, e)
+ QgsCodeEditorPython.keyPressEvent(self, e)
def focusInEvent(self, e):
pathfile = self.parent.path
@@ -560,7 +526,6 @@ class Editor(QgsCodeEditorPython):
if pathfile and self.lastModified != QFileInfo(pathfile).lastModified():
self.beginUndoAction()
self.selectAll()
- # fileReplaced = self.selectedText()
self.removeSelectedText()
file = open(pathfile, "r")
fileLines = file.readlines()
@@ -658,7 +623,6 @@ class EditorTab(QWidget):
if overwrite:
try:
permis = os.stat(path).st_mode
- # self.newEditor.lastModified = QFileInfo(path).lastModified()
os.chmod(path, permis)
except:
raise
diff --git a/python/gui/auto_generated/codeeditors/qgscodeeditorpython.sip.in b/python/gui/auto_generated/codeeditors/qgscodeeditorpython.sip.in
index 173ff5160f1..8729c3b6e06 100644
--- a/python/gui/auto_generated/codeeditors/qgscodeeditorpython.sip.in
+++ b/python/gui/auto_generated/codeeditors/qgscodeeditorpython.sip.in
@@ -62,6 +62,13 @@ Loads a ``script`` file.
Searches the selected text in the official PyQGIS online documentation.
.. versionadded:: 3.16
+%End
+
+ void toggleComment();
+%Docstring
+Toggle comment for the selected text.
+
+.. versionadded:: 3.30
%End
protected:
@@ -69,6 +76,8 @@ Searches the selected text in the official PyQGIS online documentation.
virtual void initializeLexer();
+ virtual void keyPressEvent( QKeyEvent *event );
+
protected slots:
void autoComplete();
diff --git a/python/plugins/processing/script/ScriptEditorDialog.py b/python/plugins/processing/script/ScriptEditorDialog.py
index 7626f0240ee..e67fd729bcb 100644
--- a/python/plugins/processing/script/ScriptEditorDialog.py
+++ b/python/plugins/processing/script/ScriptEditorDialog.py
@@ -92,6 +92,8 @@ class ScriptEditorDialog(BASE, WIDGET):
QgsApplication.getThemeIcon('/mActionIncreaseFont.svg'))
self.actionDecreaseFontSize.setIcon(
QgsApplication.getThemeIcon('/mActionDecreaseFont.svg'))
+ self.actionToggleComment.setIcon(
+ QgsApplication.getThemeIcon('console/iconCommentEditorConsole.svg'))
# Connect signals and slots
self.actionOpenScript.triggered.connect(self.openScript)
@@ -106,6 +108,7 @@ class ScriptEditorDialog(BASE, WIDGET):
self.actionFindReplace.toggled.connect(self.toggleSearchBox)
self.actionIncreaseFontSize.triggered.connect(self.editor.zoomIn)
self.actionDecreaseFontSize.triggered.connect(self.editor.zoomOut)
+ self.actionToggleComment.triggered.connect(self.editor.toggleComment)
self.editor.textChanged.connect(lambda: self.setHasChanged(True))
self.leFindText.returnPressed.connect(self.find)
diff --git a/python/plugins/processing/ui/DlgScriptEditor.ui b/python/plugins/processing/ui/DlgScriptEditor.ui
index 653705d5b51..a349e51835b 100644
--- a/python/plugins/processing/ui/DlgScriptEditor.ui
+++ b/python/plugins/processing/ui/DlgScriptEditor.ui
@@ -6,7 +6,7 @@
0
0
- 721
+ 600
578
@@ -120,6 +120,8 @@
+
+
@@ -240,11 +242,16 @@
true
+
+ Find && &Replace
+
Ctrl+F
+
+
- Find && &Replace
+ Toggle Comment
diff --git a/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.cpp b/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.cpp
index 814d3b558ef..8e3ad267359 100644
--- a/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.cpp
+++ b/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.cpp
@@ -21,7 +21,7 @@
#include "qgisapp.h"
#include "qgsmaptoolcapture.h"
-QgsMapToolShapeRegularPolygonAbstract::QgsMapToolShapeRegularPolygonAbstract(const QString &id, QgsMapToolCapture *parentTool )
+QgsMapToolShapeRegularPolygonAbstract::QgsMapToolShapeRegularPolygonAbstract( const QString &id, QgsMapToolCapture *parentTool )
: QgsMapToolShapeAbstract( id, parentTool )
{
}
diff --git a/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.h b/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.h
index 1cc9b0b2a18..09dd6faef39 100644
--- a/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.h
+++ b/src/app/maptools/qgsmaptoolshaperegularpolygonabstract.h
@@ -28,7 +28,7 @@ class APP_EXPORT QgsMapToolShapeRegularPolygonAbstract: public QgsMapToolShapeAb
Q_OBJECT
public:
- QgsMapToolShapeRegularPolygonAbstract(const QString &id, QgsMapToolCapture *parentTool);
+ QgsMapToolShapeRegularPolygonAbstract( const QString &id, QgsMapToolCapture *parentTool );
void clean() override;
diff --git a/src/gui/codeeditors/qgscodeeditorpython.cpp b/src/gui/codeeditors/qgscodeeditorpython.cpp
index 6dabd3499ac..0c5e0ebe34e 100644
--- a/src/gui/codeeditors/qgscodeeditorpython.cpp
+++ b/src/gui/codeeditors/qgscodeeditorpython.cpp
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
QgsCodeEditorPython::QgsCodeEditorPython( QWidget *parent, const QList &filenames, Mode mode )
: QgsCodeEditor( parent,
@@ -185,6 +186,19 @@ void QgsCodeEditorPython::initializeLexer()
runPostLexerConfigurationTasks();
}
+void QgsCodeEditorPython::keyPressEvent( QKeyEvent *event )
+{
+ const bool ctrlModifier = event->modifiers() & Qt::ControlModifier;
+
+ if ( ctrlModifier && event->key() == Qt::Key_Colon )
+ {
+ event->accept();
+ toggleComment();
+ return;
+ }
+ return QgsCodeEditor::keyPressEvent( event );
+}
+
void QgsCodeEditorPython::autoComplete()
{
switch ( autoCompletionSource() )
@@ -242,6 +256,94 @@ void QgsCodeEditorPython::searchSelectedTextInPyQGISDocs()
QDesktopServices::openUrl( QUrl( QStringLiteral( "https://qgis.org/pyqgis/%1/search.html?q=%2" ).arg( version, text ) ) );
}
+void QgsCodeEditorPython::toggleComment()
+{
+ if ( isReadOnly() )
+ {
+ return;
+ }
+
+ beginUndoAction();
+ int startLine, startPos, endLine, endPos;
+ if ( hasSelectedText() )
+ {
+ getSelection( &startLine, &startPos, &endLine, &endPos );
+ }
+ else
+ {
+ getCursorPosition( &startLine, &startPos );
+ endLine = startLine;
+ endPos = startPos;
+ }
+
+ // Check comment state and minimum indentation for each selected line
+ bool allEmpty = true;
+ bool allCommented = true;
+ int minIndentation = -1;
+ for ( int line = startLine; line <= endLine; line++ )
+ {
+ const QString stripped = text( line ).trimmed();
+ if ( !stripped.isEmpty() )
+ {
+ allEmpty = false;
+ if ( !stripped.startsWith( '#' ) )
+ {
+ allCommented = false;
+ }
+ if ( minIndentation == -1 || minIndentation > indentation( line ) )
+ {
+ minIndentation = indentation( line );
+ }
+ }
+ }
+
+ // Special case, only empty lines
+ if ( allEmpty )
+ {
+ return;
+ }
+
+ // Selection shift to keep the same selected text after a # is added/removed
+ int delta = 0;
+
+ for ( int line = startLine; line <= endLine; line++ )
+ {
+ const QString stripped = text( line ).trimmed();
+
+ // Empty line
+ if ( stripped.isEmpty() )
+ {
+ continue;
+ }
+
+ if ( !allCommented )
+ {
+ insertAt( QStringLiteral( "# " ), line, minIndentation );
+ delta = -2;
+ }
+ else
+ {
+ if ( !stripped.startsWith( '#' ) )
+ {
+ continue;
+ }
+ if ( stripped.startsWith( QLatin1String( "# " ) ) )
+ {
+ delta = 2;
+ }
+ else
+ {
+ delta = 1;
+ }
+ setSelection( line, indentation( line ), line, indentation( line ) + delta );
+ removeSelectedText();
+ }
+ }
+
+ endUndoAction();
+ setSelection( startLine, startPos - delta, endLine, endPos - delta );
+}
+
///@cond PRIVATE
//
// QgsQsciLexerPython
@@ -256,9 +358,9 @@ const char *QgsQsciLexerPython::keywords( int set ) const
{
if ( set == 1 )
{
- return "True False and as assert break class continue def del elif else except exec "
+ return "True False and as assert break class continue def del elif else except "
"finally for from global if import in is lambda None not or pass "
- "print raise return try while with yield";
+ "raise return try while with yield async await nonlocal";
}
return QsciLexerPython::keywords( set );
diff --git a/src/gui/codeeditors/qgscodeeditorpython.h b/src/gui/codeeditors/qgscodeeditorpython.h
index 1089aae306b..0f3f5459d36 100644
--- a/src/gui/codeeditors/qgscodeeditorpython.h
+++ b/src/gui/codeeditors/qgscodeeditorpython.h
@@ -84,10 +84,19 @@ class GUI_EXPORT QgsCodeEditorPython : public QgsCodeEditor
*/
void searchSelectedTextInPyQGISDocs();
+ /**
+ * Toggle comment for the selected text.
+ *
+ * \since QGIS 3.30
+ */
+ void toggleComment();
+
protected:
void initializeLexer() override;
+ virtual void keyPressEvent( QKeyEvent *event ) override;
+
protected slots:
/**