mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
[feature][console] Add toggle comment action in the python console (#50341)
Adds a toggle comment action in the Python Console and script editors
This commit is contained in:
parent
675933aa1e
commit
1c79b4927d
@ -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 + " <b>Ctrl+:</b>")
|
||||
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()
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>721</width>
|
||||
<width>600</width>
|
||||
<height>578</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -120,6 +120,8 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionIncreaseFontSize"/>
|
||||
<addaction name="actionDecreaseFontSize"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionToggleComment"/>
|
||||
</widget>
|
||||
<action name="actionOpenScript">
|
||||
<property name="text">
|
||||
@ -240,11 +242,16 @@
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Find && &Replace</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToggleComment">
|
||||
<property name="text">
|
||||
<string>Find && &Replace</string>
|
||||
<string>Toggle Comment</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
@ -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 )
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <QTextStream>
|
||||
#include <Qsci/qscilexerpython.h>
|
||||
#include <QDesktopServices>
|
||||
#include <QKeyEvent>
|
||||
|
||||
QgsCodeEditorPython::QgsCodeEditorPython( QWidget *parent, const QList<QString> &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 );
|
||||
|
@ -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:
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user