""" /*************************************************************************** Python Console for QGIS ------------------- begin : 2012-09-10 copyright : (C) 2012 by Salvatore Larosa email : lrssvtml (at) gmail (dot) com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ Some portions of code were taken from https://code.google.com/p/pydee/ """ from pathlib import Path from qgis.PyQt import uic from qgis.PyQt.QtCore import QCoreApplication, QUrl from qgis.PyQt.QtWidgets import ( QWidget, QFileDialog, QMessageBox, QTableWidgetItem, QHBoxLayout, ) from qgis.PyQt.QtGui import QIcon, QDesktopServices from qgis.core import QgsSettings, QgsApplication, QgsSettingsTree, Qgis from qgis.gui import QgsOptionsPageWidget, QgsOptionsWidgetFactory from .console_compile_apis import PrepareAPIDialog Ui_SettingsDialogPythonConsole, _ = uic.loadUiType( Path(__file__).parent / "console_settings.ui" ) class ConsoleOptionsFactory(QgsOptionsWidgetFactory): def __init__(self): super(QgsOptionsWidgetFactory, self).__init__() def icon(self): return QgsApplication.getThemeIcon("/console/mIconRunConsole.svg") def path(self): return ["ide"] def createWidget(self, parent): return ConsoleOptionsPage(parent) class ConsoleOptionsPage(QgsOptionsPageWidget): def __init__(self, parent): super().__init__(parent) self.options_widget = ConsoleOptionsWidget(parent) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setMargin(0) self.setLayout(layout) layout.addWidget(self.options_widget) self.setObjectName("consoleOptions") def apply(self): self.options_widget.accept() def helpKey(self): return "plugins/python_console.html" class ConsoleOptionsWidget(QWidget, Ui_SettingsDialogPythonConsole): def __init__(self, parent): super().__init__(parent) self.setWindowTitle( QCoreApplication.translate( "SettingsDialogPythonConsole", "Python Console Settings" ) ) self.parent = parent self.setupUi(self) # Populate the documentation Browser combobox self.contextHelpBrowser.addItem( QCoreApplication.translate( "PythonConsole", "Embedded Webview (developer tools)" ), Qgis.DocumentationBrowser.DeveloperToolsPanel, ) self.contextHelpBrowser.addItem( QCoreApplication.translate("PythonConsole", "Default System Web Browser"), Qgis.DocumentationBrowser.SystemWebBrowser, ) self.autopep8Level.setClearValue(1) self.maxLineLength.setClearValue(80) # Set up the formatter combo box self.formatter.addItems(["autopep8", "black"]) self.listPath = [] self.lineEdit.setReadOnly(True) self.restoreSettings() self.initialCheck() self.addAPIpath.setIcon(QIcon(":/images/themes/default/symbologyAdd.svg")) self.addAPIpath.setToolTip( QCoreApplication.translate("PythonConsole", "Add API path") ) self.removeAPIpath.setIcon(QIcon(":/images/themes/default/symbologyRemove.svg")) self.removeAPIpath.setToolTip( QCoreApplication.translate("PythonConsole", "Remove API path") ) self.preloadAPI.stateChanged.connect(self.initialCheck) self.addAPIpath.clicked.connect(self.loadAPIFile) self.removeAPIpath.clicked.connect(self.removeAPI) self.compileAPIs.clicked.connect(self._prepareAPI) self.formatter.currentTextChanged.connect(self.onFormatterChanged) self.onFormatterChanged() def initialCheck(self): if self.preloadAPI.isChecked(): self.enableDisable(False) else: self.enableDisable(True) def enableDisable(self, value): self.tableWidget.setEnabled(value) self.addAPIpath.setEnabled(value) self.removeAPIpath.setEnabled(value) self.groupBoxPreparedAPI.setEnabled(value) def loadAPIFile(self): settings = QgsSettings() lastDirPath = settings.value("pythonConsole/lastDirAPIPath", "", type=str) fileAPI, selected_filter = QFileDialog.getOpenFileName( self, "Open API File", lastDirPath, "API file (*.api)" ) if fileAPI: self.addAPI(fileAPI) settings.setValue("pythonConsole/lastDirAPIPath", fileAPI) def _prepareAPI(self): if self.tableWidget.rowCount() != 0: pap_file, filter = QFileDialog().getSaveFileName( self, "", "*.pap", "Prepared APIs file (*.pap)" ) else: QMessageBox.information( self, self.tr("Warning!"), self.tr("You need to add some APIs file in order to compile"), ) return if pap_file: api_lexer = "QsciLexerPython" api_files = [] count = self.tableWidget.rowCount() for i in range(0, count): api_files.append(self.tableWidget.item(i, 1).text()) api_dlg = PrepareAPIDialog(api_lexer, api_files, pap_file, self) api_dlg.show() api_dlg.activateWindow() api_dlg.raise_() api_dlg.prepareAPI() self.lineEdit.setText(pap_file) def accept(self): if not self.preloadAPI.isChecked() and not self.groupBoxPreparedAPI.isChecked(): if self.tableWidget.rowCount() == 0: QMessageBox.information( self, self.tr("Warning!"), self.tr( 'Please specify API file or check "Use preloaded API files"' ), ) return if self.groupBoxPreparedAPI.isChecked() and not self.lineEdit.text(): QMessageBox.information( self, self.tr("Warning!"), QCoreApplication.translate( "optionsDialog", 'The APIs file was not compiled, click on "Compile APIs…"', ), ) return self.saveSettings() self.listPath = [] def addAPI(self, pathAPI): count = self.tableWidget.rowCount() self.tableWidget.setColumnCount(2) self.tableWidget.insertRow(count) pathItem = QTableWidgetItem(pathAPI) pathSplit = pathAPI.split("/") apiName = pathSplit[-1][0:-4] apiNameItem = QTableWidgetItem(apiName) self.tableWidget.setItem(count, 0, apiNameItem) self.tableWidget.setItem(count, 1, pathItem) def removeAPI(self): listItemSel = self.tableWidget.selectionModel().selectedRows() for index in reversed(listItemSel): self.tableWidget.removeRow(index.row()) def saveSettings(self): settings = QgsSettings() settings.setValue("pythonConsole/preloadAPI", self.preloadAPI.isChecked()) settings.setValue( "pythonConsole/autoSaveScript", self.autoSaveScript.isChecked() ) for i in range(0, self.tableWidget.rowCount()): text = self.tableWidget.item(i, 1).text() self.listPath.append(text) settings.setValue("pythonConsole/userAPI", self.listPath) settings.setValue( "pythonConsole/autoCompThreshold", self.autoCompThreshold.value() ) settings.setValue( "pythonConsole/autoCompleteEnabled", self.groupBoxAutoCompletion.isChecked() ) settings.setValue( "pythonConsole/usePreparedAPIFile", self.groupBoxPreparedAPI.isChecked() ) settings.setValue("pythonConsole/preparedAPIFile", self.lineEdit.text()) if self.autoCompFromAPI.isChecked(): settings.setValue("pythonConsole/autoCompleteSource", "fromAPI") elif self.autoCompFromDoc.isChecked(): settings.setValue("pythonConsole/autoCompleteSource", "fromDoc") elif self.autoCompFromDocAPI.isChecked(): settings.setValue("pythonConsole/autoCompleteSource", "fromDocAPI") settings.setValue( "pythonConsole/enableObjectInsp", self.enableObjectInspector.isChecked() ) settings.setValue( "pythonConsole/autoCloseBracket", self.autoCloseBracket.isChecked() ) settings.setValue("pythonConsole/autoSurround", self.autoSurround.isChecked()) settings.setValue( "pythonConsole/autoInsertImport", self.autoInsertImport.isChecked() ) settings.setValue("pythonConsole/formatOnSave", self.formatOnSave.isChecked()) codeEditorTreeNode = QgsSettingsTree.node("gui").childNode("code-editor") pythonSettingsTreeNode = codeEditorTreeNode.childNode("python") pythonSettingsTreeNode.childSetting("sort-imports").setValue( self.sortImports.isChecked() ) pythonSettingsTreeNode.childSetting("formatter").setValue( self.formatter.currentText() ) pythonSettingsTreeNode.childSetting("autopep8-level").setValue( self.autopep8Level.value() ) pythonSettingsTreeNode.childSetting("black-normalize-quotes").setValue( self.blackNormalizeQuotes.isChecked() ) pythonSettingsTreeNode.childSetting("max-line-length").setValue( self.maxLineLength.value() ) pythonSettingsTreeNode.childSetting("external-editor").setValue( self.externalEditor.text() ) pythonSettingsTreeNode.childSetting("context-help-browser").setVariantValue( self.contextHelpBrowser.currentData().name ) codeEditorTreeNode.childSetting("context-help-hover").setValue( self.contextHelpHover.isChecked() ) def restoreSettings(self): settings = QgsSettings() self.preloadAPI.setChecked( settings.value("pythonConsole/preloadAPI", True, type=bool) ) self.lineEdit.setText( settings.value("pythonConsole/preparedAPIFile", "", type=str) ) itemTable = settings.value("pythonConsole/userAPI", []) if itemTable: self.tableWidget.setRowCount(0) for i in range(len(itemTable)): self.tableWidget.insertRow(i) self.tableWidget.setColumnCount(2) pathSplit = itemTable[i].split("/") apiName = pathSplit[-1][0:-4] self.tableWidget.setItem(i, 0, QTableWidgetItem(apiName)) self.tableWidget.setItem(i, 1, QTableWidgetItem(itemTable[i])) self.autoSaveScript.setChecked( settings.value("pythonConsole/autoSaveScript", False, type=bool) ) self.autoCompThreshold.setValue( settings.value("pythonConsole/autoCompThreshold", 2, type=int) ) self.groupBoxAutoCompletion.setChecked( settings.value("pythonConsole/autoCompleteEnabled", True, type=bool) ) self.enableObjectInspector.setChecked( settings.value("pythonConsole/enableObjectInsp", False, type=bool) ) self.autoCloseBracket.setChecked( settings.value("pythonConsole/autoCloseBracket", True, type=bool) ) self.autoSurround.setChecked( settings.value("pythonConsole/autoSurround", True, type=bool) ) self.autoInsertImport.setChecked( settings.value("pythonConsole/autoInsertImport", False, type=bool) ) codeEditorTreeNode = QgsSettingsTree.node("gui").childNode("code-editor") pythonSettingsTreeNode = codeEditorTreeNode.childNode("python") self.formatOnSave.setChecked( settings.value("pythonConsole/formatOnSave", False, type=bool) ) self.sortImports.setChecked( pythonSettingsTreeNode.childSetting("sort-imports").value() ) self.formatter.setCurrentText( pythonSettingsTreeNode.childSetting("formatter").value() ) self.autopep8Level.setValue( pythonSettingsTreeNode.childSetting("autopep8-level").value() ) self.blackNormalizeQuotes.setChecked( pythonSettingsTreeNode.childSetting("black-normalize-quotes").value() ) self.maxLineLength.setValue( pythonSettingsTreeNode.childSetting("max-line-length").value() ) browserName = pythonSettingsTreeNode.childSetting( "context-help-browser" ).valueAsVariant() try: browser = Qgis.DocumentationBrowser[browserName] except KeyError: browser = Qgis.DocumentationBrowser.DeveloperToolsPanel self.contextHelpBrowser.setCurrentIndex( self.contextHelpBrowser.findData(browser) ) self.contextHelpHover.setChecked( codeEditorTreeNode.childSetting("context-help-hover").value() ) if settings.value("pythonConsole/autoCompleteSource") == "fromDoc": self.autoCompFromDoc.setChecked(True) elif settings.value("pythonConsole/autoCompleteSource") == "fromAPI": self.autoCompFromAPI.setChecked(True) elif settings.value("pythonConsole/autoCompleteSource") == "fromDocAPI": self.autoCompFromDocAPI.setChecked(True) self.externalEditor.setText( pythonSettingsTreeNode.childSetting("external-editor").value() ) def onFormatterChanged(self): """Toggle formatter-specific options visibility when the formatter is changed""" if self.formatter.currentText() == "autopep8": self.autopep8Level.setVisible(True) self.autopep8LevelLabel.setVisible(True) self.blackNormalizeQuotes.setVisible(False) else: # black self.autopep8Level.setVisible(False) self.autopep8LevelLabel.setVisible(False) self.blackNormalizeQuotes.setVisible(True)