diff --git a/python/console.py b/python/console.py index b16c61c..e3e18f4 100755 --- a/python/console.py +++ b/python/console.py @@ -16,6 +16,14 @@ Has +- the same behaviour as command-line interactive console: - has command history, accessible using up/down keys - supports pasting of commands +Can be opened as a QWidget (top-level window, unowned) or as a QDockWidget; +/Qgis/dockPythonConsole in the registry controls this; set in Settings, Options, General tab, Open Python console in a dock window +Takes effect the next time Plugins, Python Console is chosen. It is not necessary to restart QGIS. +The editor is retained so none of its content is lost. +- as a QWidget: Plugins, Python Console will show it; it can be hidden by its Close (X) button or minimized +- as a QDockWidget: Plugins, Python Console toggles show/hide; is hidden (actually, not constructed) on QGIS start up + also, this appears in the main window context menu and can be toggled off and on there + TODO: - configuration - init commands, font, ... - python code highlighting @@ -33,19 +41,50 @@ import code _init_commands = ["from qgis.core import *", "import qgis.utils"] _console = None +_consoleEditor = None +_consoleAsDock = None def show_console(): """ called from QGIS to open the console """ global _console - if _console is None: - _console = PythonConsole(iface.mainWindow()) - _console.show() # force show even if it was restored as hidden + global _consoleEditor + global _consoleAsDock + + s = QSettings() + tempConsoleAsDock = s.value("/Qgis/dockPythonConsole",False).toBool() + + if _consoleAsDock != tempConsoleAsDock: + if _consoleEditor is None: + _consoleEditor = PythonEdit() + + if _console is not None: + if _consoleAsDock: + iface.mainWindow().removeDockWidget(_console) + else: + _console.hide() + + if tempConsoleAsDock: # re-parent the editor object + newconsole = PythonConsole(_consoleEditor, iface.mainWindow()) + else: + newconsole = PythonConsoleW(_consoleEditor) # unowned window + + newconsole.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console")) + + _console = None # then delete the old console window + _console = newconsole # and remember the new console window + _consoleAsDock = tempConsoleAsDock # save current state + + if _consoleAsDock: + _console.setVisible(not _console.isVisible()) # toggle on each call if QDockWidget else: - _console.setVisible(not _console.isVisible()) + _console.show() # always show if QWidget + _console.raise_() + _console.setWindowState( _console.windowState() & ~Qt.WindowMinimized ) + # set focus to the edit box so the user can start typing if _console.isVisible(): _console.activateWindow() - _console.edit.setFocus() + _consoleEditor.setFocus() _old_stdout = sys.stdout @@ -56,7 +95,7 @@ def clearConsole(): global _console if _console is None: return - _console.edit.clearConsole() + _consoleEditor.clearConsole() # hook for python console so all output will be redirected @@ -80,26 +119,57 @@ class QgisOutputCatcher: sys.stdout = QgisOutputCatcher() class PythonConsole(QDockWidget): - def __init__(self, parent=None): + """ as a QDockWidget """ + def __init__(self, editor, parent=None): QDockWidget.__init__(self, parent) self.setObjectName("Python Console") self.setAllowedAreas(Qt.BottomDockWidgetArea) - self.widget = QWidget() - self.l = QVBoxLayout(self.widget) - self.l.setContentsMargins(0,0,0,0) - self.edit = PythonEdit() - self.l.addWidget(self.edit) - self.setWidget(self.widget) - self.setWindowTitle(QCoreApplication.translate("PythonConsole", "Python Console")) + widget = QWidget() + l = QVBoxLayout(widget) + l.setContentsMargins(0,0,0,0) + l.addWidget(editor) + self.setWidget(widget) # try to restore position from stored main window state if not iface.mainWindow().restoreDockWidget(self): iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self) - + self.hide() # visibility toggling will show console on first use + + def sizeHint(self): + return QSize(500,300) + + def closeEvent(self, event): + QWidget.closeEvent(self, event) + +class PythonConsoleW(QWidget): + """ as a QWidget """ + def __init__(self, editor, parent=None): + QWidget.__init__(self, parent) + l = QVBoxLayout() + l.addWidget(editor) + l.setContentsMargins(0,0,0,0) + self.setLayout(l) + s = QSettings() + self.restoreGeometry(s.value("/python/console/geometry").toByteArray()) + + def __del__(self): + """ + note that this isn't called when QGIS exits, so the window geometry + is not saved at program exit + """ + s = QSettings() + s.setValue("/python/console/geometry", QVariant(self.saveGeometry())) + QWidget.__del__(self) def sizeHint(self): return QSize(500,300) def closeEvent(self, event): + """ + save settings here as well as in destructor to handle more + situations where the window geometry should be saved + """ + s = QSettings() + s.setValue("/python/console/geometry", QVariant(self.saveGeometry())) QWidget.closeEvent(self, event) diff --git a/src/app/qgsoptions.cpp b/src/app/qgsoptions.cpp index 0dd9c4b..0772442 100644 --- a/src/app/qgsoptions.cpp +++ b/src/app/qgsoptions.cpp @@ -285,6 +285,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WFlags fl ) : cbxShowTips->setChecked( settings.value( "/qgis/showTips", true ).toBool() ); cbxAttributeTableDocked->setChecked( settings.value( "/qgis/dockAttributeTable", false ).toBool() ); cbxIdentifyResultsDocked->setChecked( settings.value( "/qgis/dockIdentifyResults", false ).toBool() ); + cbxPythonConsoleDocked->setChecked( settings.value( "/qgis/dockPythonConsole", false ).toBool() ); cbxSnappingOptionsDocked->setChecked( settings.value( "/qgis/dockSnapping", false ).toBool() ); cbxAddPostgisDC->setChecked( settings.value( "/qgis/addPostgisDC", false ).toBool() ); cbxAddNewLayersToCurrentGroup->setChecked( settings.value( "/qgis/addNewLayersToCurrentGroup", false ).toBool() ); @@ -570,6 +571,7 @@ void QgsOptions::saveOptions() settings.setValue( "/qgis/dockAttributeTable", cbxAttributeTableDocked->isChecked() ); settings.setValue( "/qgis/attributeTableBehaviour", cmbAttrTableBehaviour->currentIndex() ); settings.setValue( "/qgis/dockIdentifyResults", cbxIdentifyResultsDocked->isChecked() ); + settings.setValue( "/qgis/dockPythonConsole", cbxPythonConsoleDocked->isChecked() ); settings.setValue( "/qgis/dockSnapping", cbxSnappingOptionsDocked->isChecked() ); settings.setValue( "/qgis/addPostgisDC", cbxAddPostgisDC->isChecked() ); settings.setValue( "/qgis/addNewLayersToCurrentGroup", cbxAddNewLayersToCurrentGroup->isChecked() ); diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 41b7865..90a4fa8 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -343,6 +343,13 @@ + + + Open Python console in a dock window (next time console is opened) + + + + Add PostGIS layers with double click and select in extended mode @@ -1858,6 +1865,7 @@ cbxIdentifyResultsDocked cbxSnappingOptionsDocked cbxAttributeTableDocked + cbxPythonConsoleDocked cbxAddPostgisDC cbxAddNewLayersToCurrentGroup cmbAttrTableBehaviour