Skip to content

Commit

Permalink
Remove python parameter and add context menu actions
Browse files Browse the repository at this point in the history
  • Loading branch information
YoannQDQ committed Oct 3, 2024
1 parent a891163 commit 62e2067
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 72 deletions.
9 changes: 7 additions & 2 deletions python/PyQt6/gui/auto_generated/qgisinterface.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1492,9 +1492,14 @@ Unregister a previously registered tool factory from the development/debugging t
.. versionadded:: 3.14
%End

virtual void showApiDocumentation( const QString &api = QStringLiteral( "qgis" ), bool python = true, bool embedded = true, const QString &module = QString(), const QString &object = QString() ) = 0;
virtual void showApiDocumentation( const QString &api = QStringLiteral( "pyqgis" ), bool embedded = true, const QString &object = QString(), const QString &module = QString() ) = 0;
%Docstring
Show a page of the API documentation in the embedded Dev tool webview
Show a page of the API documentation

:param api: "pyqgis" or "qgis" or "qt" or "pyqgis-search"
:param embedded: If ``True``, the documentation will be opened in the embedded devtools webview. Otherwise, use system web browser
:param object: object to show in the documentation
:param module: used only if api = "pyqgis"

.. versionadded:: 3.42
%End
Expand Down
19 changes: 11 additions & 8 deletions python/console/console_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ def __init__(self,
self.modificationChanged.connect(self.editor_tab.modified)
self.modificationAttempted.connect(self.fileReadOnly)

self.helpRequested.connect(self.console_widget.shell.help)
def showApiDocumentation(self, text):
self.console_widget.shell.showApiDocumentation(text)

def set_code_editor_widget(self, widget: QgsCodeEditorWidget):
self.code_editor_widget = widget
Expand Down Expand Up @@ -156,11 +157,15 @@ def contextMenuEvent(self, e):
runSelected.setShortcut('Ctrl+E') # spellok
menu.addAction(runSelected) # spellok

pyQGISHelpAction = QAction(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
QCoreApplication.translate("PythonConsole", "Search Selection in PyQGIS Documentation"),
menu)
pyQGISHelpAction.triggered.connect(self.searchSelectedTextInPyQGISDocs)
menu.addAction(pyQGISHelpAction)
word = self.selectedText() or self.wordAtPoint(e.pos())
if word:
context_help_action = QAction(
QgsApplication.getThemeIcon("mActionHelpContents.svg"),
QCoreApplication.translate("PythonConsole", "Context Help"),
menu)
context_help_action.triggered.connect(partial(self.console_widget.shell.showApiDocumentation, word, force_search=True))
context_help_action.setShortcut('F1')
menu.addAction(context_help_action)

start_action = QAction(QgsApplication.getThemeIcon("mActionStart.svg"),
QCoreApplication.translate("PythonConsole", "Run Script"),
Expand Down Expand Up @@ -248,7 +253,6 @@ def contextMenuEvent(self, e):
self.console_widget.openSettings)
syntaxCheckAction.setEnabled(False)
pasteAction.setEnabled(False)
pyQGISHelpAction.setEnabled(False)
cutAction.setEnabled(False)
runSelected.setEnabled(False) # spellok
copyAction.setEnabled(False)
Expand All @@ -260,7 +264,6 @@ def contextMenuEvent(self, e):
runSelected.setEnabled(True) # spellok
copyAction.setEnabled(True)
cutAction.setEnabled(True)
pyQGISHelpAction.setEnabled(True)
if not self.text() == '':
selectAllAction.setEnabled(True)
syntaxCheckAction.setEnabled(True)
Expand Down
22 changes: 13 additions & 9 deletions python/console/console_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from __future__ import annotations

import sys
from functools import partial
from typing import TYPE_CHECKING

from qgis.PyQt import sip
Expand Down Expand Up @@ -171,8 +172,6 @@ def __init__(self,
self.selectAllShortcut.setContext(Qt.ShortcutContext.WidgetWithChildrenShortcut)
self.selectAllShortcut.activated.connect(self.selectAll)

self.helpRequested.connect(self.shell_editor.help)

def on_app_exit(self):
"""
Prepares the console for a graceful close
Expand Down Expand Up @@ -241,11 +240,15 @@ def contextMenuEvent(self, e):
clearAction.triggered.connect(self.clearConsole)
menu.addAction(clearAction)

pyQGISHelpAction = QAction(QgsApplication.getThemeIcon("console/iconHelpConsole.svg"),
QCoreApplication.translate("PythonConsole", "Search Selection in PyQGIS Documentation"),
menu)
pyQGISHelpAction.triggered.connect(self.searchSelectedTextInPyQGISDocs)
menu.addAction(pyQGISHelpAction)
word = self.selectedText() or self.wordAtPoint(e.pos())
if word:
context_help_action = QAction(
QgsApplication.getThemeIcon("mActionHelpContents.svg"),
QCoreApplication.translate("PythonConsole", "Context Help"),
menu)
context_help_action.triggered.connect(partial(self.shell_editor.showApiDocumentation, word, force_search=True))
context_help_action.setShortcut('F1')
menu.addAction(context_help_action)

menu.addSeparator()
copyAction = QAction(
Expand Down Expand Up @@ -273,13 +276,11 @@ def contextMenuEvent(self, e):
runAction.setEnabled(False)
clearAction.setEnabled(False)
copyAction.setEnabled(False)
pyQGISHelpAction.setEnabled(False)
selectAllAction.setEnabled(False)
showEditorAction.setEnabled(True)
if self.hasSelectedText():
runAction.setEnabled(True)
copyAction.setEnabled(True)
pyQGISHelpAction.setEnabled(True)
if not self.text(3) == '':
selectAllAction.setEnabled(True)
clearAction.setEnabled(True)
Expand Down Expand Up @@ -315,3 +316,6 @@ def enteredSelected(self):

def widgetMessageBar(self, text: str):
self.infoBar.pushMessage(text, Qgis.MessageLevel.Info)

def showApiDocumentation(self, text):
self.shell_editor.showApiDocumentation(text)
62 changes: 42 additions & 20 deletions python/console/console_sci.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import re
import sys
import traceback
from functools import partial
from typing import (
Optional,
TYPE_CHECKING
Expand All @@ -33,8 +34,8 @@

from qgis.PyQt.Qsci import QsciScintilla
from qgis.PyQt.QtCore import Qt, QCoreApplication
from qgis.PyQt.QtGui import QKeySequence, QFontMetrics, QClipboard
from qgis.PyQt.QtWidgets import QShortcut, QApplication
from qgis.PyQt.QtGui import QKeySequence, QFontMetrics, QClipboard, QCursor
from qgis.PyQt.QtWidgets import QShortcut, QApplication, QAction
from qgis.core import (
QgsApplication,
Qgis,
Expand Down Expand Up @@ -104,31 +105,37 @@ def __parse_object(object=None):
return 'qt', module, obj
""",
r"""
def _help(object=None, api="c++", embedded=True):
def _help(object=None, api="pyqgis", embedded=True, force_search=False):
'''
Link to the C++ or PyQGIS API documentation for the given object.
If no object is given, the main PyQGIS API page is opened.
If the object is not part of the QGIS API but is a Qt object the Qt documentation is opened.
'''
if object is None:
if api == "c++":
iface.showApiDocumentation(python=True)
else:
iface.showApiDocumentation()
return
if not object:
return iface.showApiDocumentation(api, embedded=embedded)
if isinstance(object, str):
try:
object = eval(object)
except (SyntaxError, NameError):
return
if embedded and not force_search:
return iface.showApiDocumentation(api, embedded=True)
else:
return iface.showApiDocumentation("pyqgis-search", object=object, embedded=False)
obj_info = __parse_object(object)
if not obj_info:
return
if force_search or isinstance(object, str) and not embedded:
return iface.showApiDocumentation("pyqgis-search", object=object, embedded=False)
else:
return iface.showApiDocumentation(api, embedded=embedded)
obj_type, module, class_name = obj_info
iface.showApiDocumentation(obj_type, python= not api=="c++", embedded=embedded, module=module, object=class_name)
if obj_type == "qt":
api = "qt"
iface.showApiDocumentation(api, embedded=embedded, object=class_name, module=module)
""",
r"""
Expand All @@ -138,7 +145,7 @@ def _api(object=None):
If no object is given, the main API page is opened.
If the object is not part of the QGIS API but is a Qt object the Qt documentation is opened.
'''
return _help(object, api="c++")
return _help(object, api="qgis")
""",
r"""
def _pyqgis(object=None):
Expand Down Expand Up @@ -231,9 +238,9 @@ def execCommandImpl(self, cmd, show_input=True):
if cmd == "?":
self.shell.console_widget.shell_output.insertHelp()
elif cmd == '_pyqgis':
webbrowser.open("https://qgis.org/pyqgis/{}".format(version))
self.shell.showApi("pyqgis")
elif cmd == '_api':
webbrowser.open("https://qgis.org/api/{}".format('' if version == 'master' else version))
self.shell.showApi("qgis")
elif cmd == '_cookbook':
webbrowser.open(
"https://docs.qgis.org/{}/en/docs/pyqgis_developer_cookbook/".format(
Expand Down Expand Up @@ -329,8 +336,6 @@ def __init__(self, console_widget: PythonConsoleWidget):
self.sessionHistoryCleared.connect(self.on_session_history_cleared)
self.persistentHistoryCleared.connect(self.on_persistent_history_cleared)

self.helpRequested.connect(self.help)

def _setMinimumHeight(self):
font = self.lexer().defaultFont(0)
fm = QFontMetrics(font)
Expand Down Expand Up @@ -468,11 +473,28 @@ def runFile(self, filename, override_file_name: Optional[str] = None):
self._interpreter.execCommandImpl("sys.path.remove({0})".format(
QgsProcessingUtils.stringToPythonLiteral(dirname)), False)

def help(self, name):
def showApiDocumentation(self, text, force_search=False):

pythonSettingsTreeNode = QgsSettingsTree.node("gui").childNode("code-editor").childNode("python")

embedded = pythonSettingsTreeNode.childSetting('context-help-embedded').value()
api = "pyqgis" if pythonSettingsTreeNode.childSetting('context-help-pyqgis').value() else "c++"
api = "pyqgis" if pythonSettingsTreeNode.childSetting('context-help-pyqgis').value() else "qgis"

self._interpreter.execCommandImpl(f'_help({repr(text)}, api="{api}", embedded={embedded}, force_search={force_search})', show_input=False)

self._interpreter.execCommandImpl(f'_help("{name}", api="{api}", embedded={embedded})', show_input=False)
def showApi(self, api):
pythonSettingsTreeNode = QgsSettingsTree.node("gui").childNode("code-editor").childNode("python")
embedded = pythonSettingsTreeNode.childSetting('context-help-embedded').value()
self._interpreter.execCommandImpl(f'_help(api="{api}", embedded={embedded})', show_input=False)

def populateContextMenu(self, menu):

word = self.selectedText() or self.wordAtPoint(self.mapFromGlobal(QCursor.pos()))
if word:
context_help_action = QAction(
QgsApplication.getThemeIcon("mActionHelpContents.svg"),
QCoreApplication.translate("PythonConsole", "Context Help"),
menu)
context_help_action.triggered.connect(partial(self.showApiDocumentation, word, force_search=True))
context_help_action.setShortcut('F1')
menu.addAction(context_help_action)
9 changes: 7 additions & 2 deletions python/gui/auto_generated/qgisinterface.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1492,9 +1492,14 @@ Unregister a previously registered tool factory from the development/debugging t
.. versionadded:: 3.14
%End

virtual void showApiDocumentation( const QString &api = QStringLiteral( "qgis" ), bool python = true, bool embedded = true, const QString &module = QString(), const QString &object = QString() ) = 0;
virtual void showApiDocumentation( const QString &api = QStringLiteral( "pyqgis" ), bool embedded = true, const QString &object = QString(), const QString &module = QString() ) = 0;
%Docstring
Show a page of the API documentation in the embedded Dev tool webview
Show a page of the API documentation

:param api: "pyqgis" or "qgis" or "qt" or "pyqgis-search"
:param embedded: If ``True``, the documentation will be opened in the embedded devtools webview. Otherwise, use system web browser
:param object: object to show in the documentation
:param module: used only if api = "pyqgis"

.. versionadded:: 3.42
%End
Expand Down
51 changes: 26 additions & 25 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12940,12 +12940,12 @@ void QgisApp::helpContents()

void QgisApp::apiDocumentation()
{
showApiDocumentation( "qgis", false, false );
showApiDocumentation( "qgis", false );
}

void QgisApp::pyQgisApiDocumentation()
{
showApiDocumentation( "qgis", true, false );
showApiDocumentation( "pyqgis", false );
}

void QgisApp::reportaBug()
Expand Down Expand Up @@ -13090,46 +13090,46 @@ void QgisApp::unregisterDevToolFactory( QgsDevToolWidgetFactory *factory )
}


void QgisApp::showApiDocumentation( const QString &api, bool python, bool embedded, const QString &module, const QString &object )
void QgisApp::showApiDocumentation( const QString &api, bool embedded, const QString &object, const QString &module )
{
bool useQgisDocDirectory = false;
QString baseUrl;
QString version;

if ( api == "qt" )
{
const QStringList parts = QString( qVersion() ).split( "." );
baseUrl = QString( "https://doc.qt.io/qt-%1.%2/" ).arg( parts[0], parts[1] );
version = QString( qVersion() ).split( '.' ).mid( 0, 2 ).join( '.' );
baseUrl = QString( "https://doc.qt.io/qt-%1/" ).arg( version );
}
else if ( api == "qgis" )
else if ( api.contains( "qgis" ) )
{
// Get the current QGIS version
QString version;
if ( Qgis::version().toLower().contains( QStringLiteral( "master" ) ) )
{
version = QStringLiteral( "master" );
}
else
{
version = Qgis::version().left( 4 );
version = QString( Qgis::version() ).split( '.' ).mid( 0, 2 ).join( '.' );
}

if ( python )
if ( api.contains( "pyqgis" ) )
{
QgsSettings settings;
baseUrl = settings.value( QStringLiteral( "qgis/PyQgisApiUrl" ),
QString( "https://qgis.org/pyqgis/%1" ).arg( version ) ).toString();
QString( "https://qgis.org/pyqgis/%1/" ).arg( version ) ).toString();
}
else
{
if ( QFileInfo::exists( QgsApplication::pkgDataPath() + "/doc/api/index.html" ) )
{
useQgisDocDirectory = true;
baseUrl = "api/index.html";
baseUrl = "api/";
}
else
{
QgsSettings settings;
baseUrl = settings.value( QStringLiteral( "qgis/QgisApiUrl" ),
QString( "https://qgis.org/api/%1" ).arg( version ) ).toString();
QString( "https://qgis.org/api/%1/" ).arg( version ) ).toString();
}
}
}
Expand All @@ -13142,24 +13142,25 @@ void QgisApp::showApiDocumentation( const QString &api, bool python, bool embedd
QString url;
if ( object.isEmpty() )
{
url = baseUrl;
url = baseUrl == "api/" ? baseUrl + "index.html" : baseUrl;
}
else
{
if ( api == QStringLiteral( "qgis" ) )
if ( api == QStringLiteral( "pyqgis" ) )
{
if ( python ) // pyQGIS
{
url = baseUrl + QString( "/%1/%2.html" ).arg( module, object );
}
else
{
url = baseUrl + QString( "/class%1.html" ).arg( object );
}
url = baseUrl + QString( "%1/%2.html" ).arg( module, object );
}
else if ( api == QStringLiteral( "pyqgis-search" ) )
{
url = baseUrl + QString( "search.html?q=%2" ).arg( object );
}
else if ( api == QStringLiteral( "qgis" ) )
{
url = baseUrl + QString( "class%1.html" ).arg( object );
}
else // Qt
{
url = baseUrl + QString( "/%1.html" ).arg( object.toLower() );
url = baseUrl + QString( "%1.html" ).arg( object.toLower() );
}
}

Expand All @@ -13174,7 +13175,7 @@ void QgisApp::showApiDocumentation( const QString &api, bool python, bool embedd
}
else
{
openURL( baseUrl, useQgisDocDirectory );
openURL( url, useQgisDocDirectory );
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/qgisapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
void unregisterDevToolFactory( QgsDevToolWidgetFactory *factory );

//! Show a page of the API documentation
void showApiDocumentation( const QString &api, bool python, bool embedded, const QString &module = QString(), const QString &object = QString() );
void showApiDocumentation( const QString &api, bool embedded, const QString &object = QString(), const QString &module = QString() );

/**
* Register a new application exit blocker, which can be used to prevent the QGIS application
Expand Down
Loading

0 comments on commit 62e2067

Please sign in to comment.