[QGIS Commit] r12725 - in trunk/qgis: python src/app src/python

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sat Jan 9 19:10:23 EST 2010


Author: wonder
Date: 2010-01-09 19:10:23 -0500 (Sat, 09 Jan 2010)
New Revision: 12725

Added:
   trunk/qgis/python/console.py
Removed:
   trunk/qgis/src/app/qgspythondialog.cpp
   trunk/qgis/src/app/qgspythondialog.h
Modified:
   trunk/qgis/python/CMakeLists.txt
   trunk/qgis/python/utils.py
   trunk/qgis/src/app/CMakeLists.txt
   trunk/qgis/src/app/qgisapp.cpp
   trunk/qgis/src/app/qgisapp.h
   trunk/qgis/src/python/qgspythonutils.h
   trunk/qgis/src/python/qgspythonutilsimpl.cpp
   trunk/qgis/src/python/qgspythonutilsimpl.h
Log:
Converted Python console from C++ to Python.
This cleans python support a bit and makes a starting point for further improvements of the console.

It's possible to run the console also outside QGIS:

import qgis.console
qgis.console.show_console()



Modified: trunk/qgis/python/CMakeLists.txt
===================================================================
--- trunk/qgis/python/CMakeLists.txt	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/python/CMakeLists.txt	2010-01-10 00:10:23 UTC (rev 12725)
@@ -99,6 +99,12 @@
 ENDIF (BINDINGS_GLOBAL_INSTALL)
 
 
+# python console
+PYQT4_WRAP_UI(PYUI_FILE ${CMAKE_SOURCE_DIR}/src/ui/qgspythondialog.ui)
+
+ADD_CUSTOM_TARGET(pythonconsole ALL DEPENDS ${PYUI_FILE})
+
+
 # Step 4: install built libs to python's site packages
-INSTALL(FILES __init__.py utils.py ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
+INSTALL(FILES __init__.py utils.py console.py ${PYUI_FILE} ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
 

Added: trunk/qgis/python/console.py
===================================================================
--- trunk/qgis/python/console.py	                        (rev 0)
+++ trunk/qgis/python/console.py	2010-01-10 00:10:23 UTC (rev 12725)
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+import traceback
+import sys
+
+from ui_qgspythondialog import Ui_QgsPythonDialog
+
+_console = None
+
+
+def show_console():
+  """ called from QGIS to open the console """
+  global _console
+  if _console is None:
+    _console = QgsPythonConsole()
+  _console.show()
+  _console.raise_()
+  _console.setWindowState( _console.windowState() & ~Qt.WindowMinimized )
+  _console.activateWindow()
+
+
+
+_old_stdout = sys.stdout
+
+class QgisOutputCatcher:
+  def __init__(self):
+    self.data = ''
+  def write(self, stuff):
+    self.data += stuff
+  def get_and_clean_data(self):
+    tmp = self.data
+    self.data = ''
+    return tmp
+  def flush(self):
+    pass
+  
+def installConsoleHook():
+  sys.stdout = QgisOutputCatcher()
+
+def uninstallConsoleHook():
+  sys.stdout = _old_stdout
+
+
+
+class ConsoleHistory(object):
+  def __init__(self):
+    self.cmds = []
+    self.pos = 0
+    self.active_cmd = u"" # active, not yet entered command
+
+  def add_cmd(self, command):
+    if len(command) != 0:
+      self.cmds.append(command)
+      self.pos = len(self.cmds)
+      self.active_cmd = u""
+
+  def get_previous_cmd(self, current):
+    if self.pos == 0:
+      return None
+
+    if self.pos == len(self.cmds):
+      self.active_cmd = current
+    else:
+      self.cmds[self.pos] = current
+
+    self.pos -= 1
+    return self.cmds[self.pos]
+
+  def get_next_cmd(self, current):
+
+    # if we're at active (last) command, don't move
+    if self.pos == len(self.cmds):
+      return None
+      
+    self.cmds[self.pos] = current
+    self.pos += 1
+
+    if self.pos == len(self.cmds):
+      return self.active_cmd
+    else:
+      return self.cmds[self.pos]
+
+
+
+class QgsPythonConsole(QWidget, Ui_QgsPythonDialog):
+  def __init__(self, parent=None):
+    QWidget.__init__(self, parent)
+
+    self.setupUi(self)
+
+    # minimize button was not enabled on mac
+    self.setWindowFlags( self.windowFlags() | Qt.WindowMinimizeButtonHint )
+
+    self.history = ConsoleHistory()
+
+    self.console_globals = {}
+  
+  def escapeHtml(self, text):
+    return text.replace("<", "&lt;").replace(">", "&gt;")
+
+  @pyqtSlot()
+  def on_pbnPrev_clicked(self):
+    cmd = self.history.get_previous_cmd( self.getCommand() )
+    if cmd is not None:
+      self.edtCmdLine.setText(cmd)
+
+  @pyqtSlot()
+  def on_pbnNext_clicked(self):
+    cmd = self.history.get_next_cmd( self.getCommand() )
+    if cmd is not None:
+      self.edtCmdLine.setText(cmd)
+
+  def getCommand(self):
+    return unicode(self.edtCmdLine.toPlainText())
+
+  def execute(self, single):
+    command = self.getCommand()
+
+    self.history.add_cmd(command)
+
+    try:
+      # run command
+      code = compile(command, '<console>', 'single' if single else 'exec')
+      res = eval(code, self.console_globals)
+      result = unicode( res ) if res is not None else u''
+      
+      # get standard output
+      output = sys.stdout.get_and_clean_data()
+
+      #_old_stdout.write("cmd: '"+command+"'\n")
+      #_old_stdout.write("res: '"+result+"'\n")
+      #_old_stdout.write("out: '"+output+"'\n")
+      #_old_stdout.flush()
+
+      # escape the result so python objects display properly and
+      # we can still use html output to get nicely formatted display
+      output = self.escapeHtml( output ) + self.escapeHtml( result )
+      if len(output) != 0:
+        output += "<br>"
+
+    except Exception, e:
+      #lst = traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)
+      lst = traceback.format_exception_only(sys.exc_type, sys.exc_value)
+      errorText = "<br>".join(map(self.escapeHtml, lst))
+      output = "<font color=\"red\">" + errorText + "</font><br>"
+
+    s = "<b><font color=\"green\">&gt;&gt;&gt;</font> " + self.escapeHtml( command ) + "</b><br>" + output
+    self.edtCmdLine.setText("")
+
+    self.txtHistory.moveCursor(QTextCursor.End)
+    self.txtHistory.insertHtml(s)
+    self.txtHistory.moveCursor(QTextCursor.End)
+    self.txtHistory.ensureCursorVisible()
+
+  @pyqtSlot()
+  def on_pbnExecute_clicked(self):
+    self.execute(False)
+
+  @pyqtSlot()
+  def on_pbnEval_clicked(self):
+    self.execute(True)
+
+  def showEvent(self, event):
+    QWidget.showEvent(self, event)
+    installConsoleHook()
+
+  def closeEvent(self, event):
+    uninstallConsoleHook()
+    QWidget.closeEvent(self, event)
+
+
+if __name__ == '__main__':
+  import sys
+  a = QApplication(sys.argv)
+  w = QgsPythonConsole()
+  w.show()
+  a.exec_()

Modified: trunk/qgis/python/utils.py
===================================================================
--- trunk/qgis/python/utils.py	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/python/utils.py	2010-01-10 00:10:23 UTC (rev 12725)
@@ -56,40 +56,6 @@
 
 
 #######################
-# CONSOLE OUTPUT
-
-old_stdout = sys.stdout
-console_output = None
-
-# hook for python console so all output will be redirected
-# and then shown in console
-def console_displayhook(obj):
-  global console_output
-  console_output = obj
-
-class QgisOutputCatcher:
-  def __init__(self):
-    self.data = ''
-  def write(self, stuff):
-    self.data += stuff
-  def get_and_clean_data(self):
-    tmp = self.data
-    self.data = ''
-    return tmp
-  def flush(self):
-    pass
-  
-
-def installConsoleHooks():
-  sys.displayhook = console_displayhook
-  sys.stdout = QgisOutputCatcher()
-
-def uninstallConsoleHooks():
-  sys.displayhook = sys.__displayhook__
-  sys.stdout = old_stdout
-
-
-#######################
 # PLUGINS
 
 # dictionary of plugins

Modified: trunk/qgis/src/app/CMakeLists.txt
===================================================================
--- trunk/qgis/src/app/CMakeLists.txt	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/app/CMakeLists.txt	2010-01-10 00:10:23 UTC (rev 12725)
@@ -60,7 +60,6 @@
   qgspluginmanager.cpp
   qgspluginmetadata.cpp
   qgspluginregistry.cpp
-  qgspythondialog.cpp
   qgsprojectproperties.cpp
   qgsrasterlayerproperties.cpp
   qgssearchquerybuilder.cpp
@@ -162,7 +161,6 @@
   qgsoptions.h
   qgspastetransformations.h
   qgspluginmanager.h
-  qgspythondialog.h
   qgsprojectproperties.h
   qgsrasterlayerproperties.h
   qgssearchquerybuilder.h

Modified: trunk/qgis/src/app/qgisapp.cpp
===================================================================
--- trunk/qgis/src/app/qgisapp.cpp	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/app/qgisapp.cpp	2010-01-10 00:10:23 UTC (rev 12725)
@@ -197,7 +197,6 @@
 #include "qgsspatialitesourceselect.h"
 #endif
 
-#include "qgspythondialog.h"
 #include "qgspythonutils.h"
 
 #ifndef WIN32
@@ -329,7 +328,6 @@
 QgisApp::QgisApp( QSplashScreen *splash, QWidget * parent, Qt::WFlags fl )
     : QMainWindow( parent, fl ),
     mSplash( splash ),
-    mPythonConsole( NULL ),
     mPythonUtils( NULL )
 #ifdef HAVE_QWT
     ,
@@ -507,7 +505,6 @@
   delete mMapTools.mAddIsland;
   delete mMapTools.mNodeTool;
 
-  delete mPythonConsole;
   delete mPythonUtils;
 
   deletePrintComposers();
@@ -1110,12 +1107,16 @@
   if ( !mPythonUtils || !mPythonUtils->isEnabled() )
     return;
 
-  if ( mPythonConsole == NULL )
-    mPythonConsole = new QgsPythonDialog( mQgisInterface, mPythonUtils );
-  mPythonConsole->show();
-  mPythonConsole->raise();
-  mPythonConsole->setWindowState( mPythonConsole->windowState() & ~Qt::WindowMinimized );
-  mPythonConsole->activateWindow();
+  bool res = mPythonUtils->runStringUnsafe(
+               "import qgis.console\n"
+               "qgis.console.show_console()\n", false );
+
+  if ( !res )
+  {
+    QString className, text;
+    mPythonUtils->getError( className, text );
+    QMessageBox::critical( this, tr( "Error" ), tr( "Failed to open Python console:" ) + "\n" + className + ": " + text );
+  }
 }
 
 void QgisApp::createActionGroups()

Modified: trunk/qgis/src/app/qgisapp.h
===================================================================
--- trunk/qgis/src/app/qgisapp.h	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/app/qgisapp.h	2010-01-10 00:10:23 UTC (rev 12725)
@@ -53,7 +53,6 @@
 class QgsMapTool;
 class QgsPoint;
 class QgsProviderRegistry;
-class QgsPythonDialog;
 class QgsPythonUtils;
 class QgsRasterLayer;
 class QgsRectangle;
@@ -1045,7 +1044,6 @@
     //!flag to indicate that the previous screen mode was 'maximised'
     bool mPrevScreenModeMaximized;
 
-    QgsPythonDialog* mPythonConsole;
     QgsPythonUtils* mPythonUtils;
 
     static QgisApp *smInstance;

Deleted: trunk/qgis/src/app/qgspythondialog.cpp
===================================================================
--- trunk/qgis/src/app/qgspythondialog.cpp	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/app/qgspythondialog.cpp	2010-01-10 00:10:23 UTC (rev 12725)
@@ -1,137 +0,0 @@
-/***************************************************************************
-    qgspythondialog.h - dialog with embedded python console
-    ---------------------
-    begin                : October 2006
-    copyright            : (C) 2006 by Martin Dobias
-    email                : wonder.sk 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.                                   *
- *                                                                         *
- ***************************************************************************/
-/* $Id$ */
-
-#include "qgspythondialog.h"
-#include "qgspythonutils.h"
-#include "qgslogger.h"
-
-#include <QShowEvent>
-#include <QCloseEvent>
-
-QgsPythonDialog::QgsPythonDialog( QgisInterface* pIface, QgsPythonUtils* pythonUtils, QWidget *parent )
-    : QDialog( parent )
-{
-  setupUi( this );
-#ifdef Q_WS_MAC
-  // Qt4.3+ bug?: Mac window minimize control isn't enabled
-  setWindowFlags( windowFlags() | Qt::WindowMinimizeButtonHint );
-#endif
-  mIface = pIface;
-  mPythonUtils = pythonUtils;
-
-  pos = 0;
-}
-
-QgsPythonDialog::~QgsPythonDialog()
-{
-}
-
-QString QgsPythonDialog::escapeHtml( QString text )
-{
-  return text.replace( "<", "&lt;" ).replace( ">", "&gt;" );
-}
-
-void QgsPythonDialog::on_pbnPrev_clicked()
-{
-  if ( pos > 0 )
-  {
-    if ( pos == history.size() )
-      history << edtCmdLine->toPlainText();
-    else
-      history[pos] = edtCmdLine->toPlainText();
-    pos--;
-    edtCmdLine->setText( history[pos] );
-  }
-}
-
-void QgsPythonDialog::on_pbnNext_clicked()
-{
-  if ( pos < history.size() - 1 )
-  {
-    history[pos] = edtCmdLine->toPlainText();
-    pos++;
-    edtCmdLine->setText( history[pos] );
-  }
-}
-
-void QgsPythonDialog::execute( bool single )
-{
-  QString command = edtCmdLine->toPlainText();
-
-  QgsDebugMsg( QString( "command: |%1| %2" ).arg( command ).arg( single ) );
-
-  if ( !command.isEmpty() )
-  {
-    history << command;
-    pos = history.size();
-  }
-
-  QString output;
-
-  // when using Py_single_input the return value will be always null
-  // we're using custom hooks for output and exceptions to show output in console
-  if ( mPythonUtils->runStringUnsafe( command, single ) )
-  {
-    mPythonUtils->evalString( "sys.stdout.get_and_clean_data()", output );
-    QString result = mPythonUtils->getResult();
-    // escape the result so python objects display properly and
-    // we can still use html output to get nicely formatted display
-    output = escapeHtml( output ) + escapeHtml( result );
-
-    if ( !output.isEmpty() )
-      output += "<br>";
-  }
-  else
-  {
-    QString className, errorText;
-    mPythonUtils->getError( className, errorText );
-
-    output = "<font color=\"red\">" + escapeHtml( className ) + ": " + escapeHtml( errorText ) + "</font><br>";
-  }
-
-  QString str = "<b><font color=\"green\">&gt;&gt;&gt;</font> " + escapeHtml( command ) + "</b><br>" + output;
-
-  edtCmdLine->setText( "" );
-
-  txtHistory->moveCursor( QTextCursor::End );
-  txtHistory->insertHtml( str );
-  txtHistory->moveCursor( QTextCursor::End );
-  txtHistory->ensureCursorVisible();
-}
-
-void QgsPythonDialog::on_pbnExecute_clicked()
-{
-  execute( false );
-}
-
-void QgsPythonDialog::on_pbnEval_clicked()
-{
-  execute( true );
-}
-
-void QgsPythonDialog::showEvent( QShowEvent* event )
-{
-  QDialog::showEvent( event );
-
-  mPythonUtils->installConsoleHooks();
-}
-
-void QgsPythonDialog::closeEvent( QCloseEvent* event )
-{
-  mPythonUtils->uninstallConsoleHooks();
-
-  QDialog::closeEvent( event );
-}

Deleted: trunk/qgis/src/app/qgspythondialog.h
===================================================================
--- trunk/qgis/src/app/qgspythondialog.h	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/app/qgspythondialog.h	2010-01-10 00:10:23 UTC (rev 12725)
@@ -1,60 +0,0 @@
-/***************************************************************************
-    qgspythondialog.h - dialog with embedded python console
-    ---------------------
-    begin                : October 2006
-    copyright            : (C) 2006 by Martin Dobias
-    email                : wonder.sk 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.                                   *
- *                                                                         *
- ***************************************************************************/
-/* $Id$ */
-
-#ifndef QGSPYTHONDIALOG_H
-#define QGSPYTHONDIALOG_H
-
-#include "ui_qgspythondialog.h"
-
-class QgisInterface;
-class QgsPythonUtils;
-class QCloseEvent;
-class QShowEvent;
-
-class QgsPythonDialog : public QDialog, private Ui::QgsPythonDialog
-{
-    Q_OBJECT
-
-  public:
-    QgsPythonDialog( QgisInterface* pIface, QgsPythonUtils* pythonUtils, QWidget *parent = 0 );
-
-    ~QgsPythonDialog();
-
-    QString escapeHtml( QString text );
-
-  public slots:
-
-    void on_pbnPrev_clicked();
-    void on_pbnExecute_clicked();
-    void on_pbnEval_clicked();
-    void on_pbnNext_clicked();
-
-  protected:
-
-    void closeEvent( QCloseEvent *event );
-    void showEvent( QShowEvent *event );
-
-  private:
-    void execute( bool single );
-
-    QgisInterface* mIface;
-    QgsPythonUtils* mPythonUtils;
-
-    QStringList history;
-    int pos;
-};
-
-#endif

Modified: trunk/qgis/src/python/qgspythonutils.h
===================================================================
--- trunk/qgis/src/python/qgspythonutils.h	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/python/qgspythonutils.h	2010-01-10 00:10:23 UTC (rev 12725)
@@ -56,17 +56,6 @@
 
     virtual bool evalString( const QString& command, QString& result ) = 0;
 
-    //! change displayhook and excepthook
-    //! our hooks will just save the result to special variables
-    //! and those can be used in the program
-    virtual void installConsoleHooks() = 0;
-
-    //! get back to the original settings (i.e. write output to stdout)
-    virtual void uninstallConsoleHooks() = 0;
-
-    //! get result from the last statement as a string
-    virtual QString getResult() = 0;
-
     //! get information about error to the supplied arguments
     //! @return false if there was no python error
     virtual bool getError( QString& errorClassName, QString& errorText ) = 0;

Modified: trunk/qgis/src/python/qgspythonutilsimpl.cpp
===================================================================
--- trunk/qgis/src/python/qgspythonutilsimpl.cpp	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/python/qgspythonutilsimpl.cpp	2010-01-10 00:10:23 UTC (rev 12725)
@@ -121,17 +121,8 @@
   runString( "qgis.utils.uninstallErrorHook()" );
 }
 
-void QgsPythonUtilsImpl::installConsoleHooks()
-{
-  runString( "qgis.utils.installConsoleHooks()" );
-}
 
-void QgsPythonUtilsImpl::uninstallConsoleHooks()
-{
-  runString( "qgis.utils.uninstallConsoleHooks()" );
-}
 
-
 bool QgsPythonUtilsImpl::runStringUnsafe( const QString& command, bool single )
 {
   // TODO: convert special characters from unicode strings u"..." to \uXXXX
@@ -299,14 +290,6 @@
   return true;
 }
 
-QString QgsPythonUtilsImpl::getResult()
-{
-  QString res;
-  evalString( "qgis.utils.console_output", res );
-  // clear output
-  runString( "qgis.utils.console_output = None" );
-  return res;
-}
 
 QString QgsPythonUtilsImpl::PyObjectToQString( PyObject* obj )
 {
@@ -368,25 +351,7 @@
   return "(qgis error)";
 }
 
-QString QgsPythonUtilsImpl::getVariableFromMain( QString name )
-{
-  PyObject* obj;
-  QString output;
 
-  // get the result
-  obj = PyDict_GetItemString( mMainDict, name.toUtf8() ); // obj is borrowed reference
-
-  if ( obj != NULL && obj != Py_None )
-  {
-    output = PyObjectToQString( obj );
-  }
-
-  // erase result
-  PyDict_SetItemString( mMainDict, name.toUtf8(), Py_None );
-
-  return output;
-}
-
 bool QgsPythonUtilsImpl::evalString( const QString& command, QString& result )
 {
   PyObject* res = PyRun_String( command.toUtf8().data(), Py_eval_input, mMainDict, mMainDict );

Modified: trunk/qgis/src/python/qgspythonutilsimpl.h
===================================================================
--- trunk/qgis/src/python/qgspythonutilsimpl.h	2010-01-09 23:10:34 UTC (rev 12724)
+++ trunk/qgis/src/python/qgspythonutilsimpl.h	2010-01-10 00:10:23 UTC (rev 12725)
@@ -68,22 +68,6 @@
     //! @return false if there was no python error
     bool getError( QString& errorClassName, QString& errorText );
 
-    //! get variable from main dictionary
-    QString getVariableFromMain( QString name );
-
-    /* python console related functions */
-
-    //! change displayhook and excepthook
-    //! our hooks will just save the result to special variables
-    //! and those can be used in the program
-    void installConsoleHooks();
-
-    //! get back to the original settings (i.e. write output to stdout)
-    void uninstallConsoleHooks();
-
-    //! get result from the last statement as a string
-    QString getResult();
-
     /* plugins related functions */
 
     //! return current path for python plugins



More information about the QGIS-commit mailing list