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

svn_qgis at osgeo.org svn_qgis at osgeo.org
Sat Nov 7 07:04:42 EST 2009


Author: wonder
Date: 2009-11-07 07:04:42 -0500 (Sat, 07 Nov 2009)
New Revision: 11950

Added:
   trunk/qgis/python/utils.py
Modified:
   trunk/qgis/python/CMakeLists.txt
   trunk/qgis/src/python/qgspythonutilsimpl.cpp
Log:
Moved most of the python support code to a new module qgis.utils
This will enable more flexibility in plugins handling
Hopefully it doesn't break anything :-)


Modified: trunk/qgis/python/CMakeLists.txt
===================================================================
--- trunk/qgis/python/CMakeLists.txt	2009-11-07 11:41:45 UTC (rev 11949)
+++ trunk/qgis/python/CMakeLists.txt	2009-11-07 12:04:42 UTC (rev 11950)
@@ -89,5 +89,5 @@
 
 
 # Step 4: install built libs to python's site packages
-INSTALL(FILES __init__.py ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
+INSTALL(FILES __init__.py utils.py ${CMAKE_CURRENT_BINARY_DIR}/qgisconfig.py ${BINDINGS_LIBS} DESTINATION ${SITE_PKG_PATH}/qgis)
 

Added: trunk/qgis/python/utils.py
===================================================================
--- trunk/qgis/python/utils.py	                        (rev 0)
+++ trunk/qgis/python/utils.py	2009-11-07 12:04:42 UTC (rev 11950)
@@ -0,0 +1,160 @@
+"""
+QGIS utilities module
+
+"""
+
+from PyQt4.QtCore import QCoreApplication
+import sys
+import traceback
+
+
+#######################
+# ERROR HANDLING
+
+def showException(type, value, tb, msg):
+  lst = traceback.format_exception(type, value, tb)
+  if msg == None:
+    msg = QCoreApplication.translate('Python', 'An error has occured while executing Python code:')
+  txt = '<font color="red">%s</font><br><br>' % msg
+  for s in lst:
+    txt += s
+  txt += '<br>%s<br>%s<br><br>' % (QCoreApplication.translate('Python','Python version:'), sys.version)
+  txt += '%s %s' % (QCoreApplication.translate('Python','Python path:'), str(sys.path))
+  txt = txt.replace('\n', '<br>')
+  txt = txt.replace('  ', '&nbsp; ') # preserve whitespaces for nicer output
+
+  from qgis.core import QgsMessageOutput
+  msg = QgsMessageOutput.createMessageOutput()
+  msg.setTitle(QCoreApplication.translate('Python', 'Python error'))
+  msg.setMessage(txt, QgsMessageOutput.MessageHtml)
+  msg.showMessage()
+
+def qgis_excepthook(type, value, tb):
+  showException(type, value, tb, None)
+
+def installErrorHook():
+  sys.excepthook = qgis_excepthook
+
+def uninstallErrorHook():
+  sys.excepthook = sys.__excepthook__
+
+# install error hook() on module load
+installErrorHook()
+
+# initialize 'iface' object
+iface = None
+
+def initInterface(pointer):
+  from qgis.gui import QgisInterface
+  from sip import wrapinstance
+  global iface
+  iface = wrapinstance(pointer, QgisInterface)
+
+
+#######################
+# 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):
+  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 installConsoleHooks():
+  sys.displayhook = console_displayhook
+  sys.stdout = QgisOutputCatcher()
+
+def uninstallConsoleHooks():
+  sys.displayhook = sys.__displayhook__
+  sys.stdout = old_stdout
+
+
+#######################
+# PLUGINS
+
+# dictionary of plugins
+plugins = {}
+
+def pluginMetadata(packageName, fct):
+  """ fetch metadata from a plugin """
+  try:
+    package = sys.modules[packageName]
+    return getattr(package, fct)()
+  except:
+    return "__error__"
+
+def loadPlugin(packageName):
+  """ load plugin's package and ensure that plugin is reloaded when changed """
+
+  try:
+    __import__(packageName)
+    return True
+  except:
+    pass # continue...
+
+  # snake in the grass, we know it's there
+  sys.path_importer_cache.clear()
+
+  # retry
+  try:
+    __import__(packageName)
+    reload(packageName)
+    return True
+  except:
+    msgTemplate = QCoreApplication.translate("Python", "Couldn't load plugin '%1' from ['%2']")
+    msg = msgTemplate.arg(packageName).arg("', '".join(sys.path))
+    showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
+    return False
+
+
+def startPlugin(packageName):
+  """ initialize the plugin """
+  global plugins, iface
+
+  package = sys.modules[packageName]
+
+  errMsg = QCoreApplication.translate("Python", "Couldn't load plugin %1" ).arg(packageName)
+
+  # create an instance of the plugin
+  try:
+    plugins[packageName] = package.classFactory(iface)
+  except:
+    msg = QCoreApplication.translate("Python", "%1 due an error when calling its classFactory() method").arg(errMsg)
+    showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
+    return False
+
+  # initGui
+  try:
+    plugins[packageName].initGui()
+  except:
+    msg = QCoreApplication.translate("Python", "%1 due an error when calling its initGui() method" ).arg( errMsg )
+    showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
+    return False
+
+  return True
+
+
+def unloadPlugin(packageName):
+  """ unload and delete plugin! """
+  global plugins
+
+  try:
+    plugins[packageName].unload()
+    del plugins[packageName]
+    return True
+  except Exception, e:
+    errMsg = QCoreApplication.translate("Python", "Error while unloading plugin %1").arg(packageName)
+    showException(sys.exc_type, sys.exc_value, sys.exc_traceback, msg)
+    return False

Modified: trunk/qgis/src/python/qgspythonutilsimpl.cpp
===================================================================
--- trunk/qgis/src/python/qgspythonutilsimpl.cpp	2009-11-07 11:41:45 UTC (rev 11949)
+++ trunk/qgis/src/python/qgspythonutilsimpl.cpp	2009-11-07 12:04:42 UTC (rev 11950)
@@ -61,9 +61,6 @@
   // also add path to plugins
   runString( "sys.path = [\"" + pythonPath() + "\", \"" + homePluginsPath()  + "\", \"" + pluginsPath() + "\"] + sys.path" );
 
-  runString( "import traceback" ); // for formatting stack traces
-  runString( "import __main__" ); // to access explicitly global variables
-
   // import SIP
   if ( !runString( "from sip import wrapinstance, unwrapinstance",
                    QObject::tr( "Couldn't load SIP module." ) + "\n" + QObject::tr( "Python support will be disabled." ) ) )
@@ -88,49 +85,16 @@
     return;
   }
 
-  // hook that will show information and traceback in message box
-  runString(
-    "def qgis_except_hook_msg(type, value, tb, msg):\n"
-    "  lst = traceback.format_exception(type, value, tb)\n"
-    "  if msg == None: msg = '" + QObject::tr( "An error has occured while executing Python code:" ).replace( "'", "\\'" ) + "'\n"
-    "  txt = '<font color=\"red\">'+msg+'</font><br><br>'\n"
-    "  for s in lst:\n"
-    "    txt += s\n"
-    "  txt += '<br>" + QObject::tr( "Python version:" ).replace( "'", "\\'" ) + "<br>' + sys.version + '<br><br>'\n"
-    "  txt += '" + QObject::tr( "Python path:" ).replace( "'", "\\'" ) + "' + str(sys.path)\n"
-    "  txt = txt.replace('\\n', '<br>')\n"
-    "  txt = txt.replace('  ', '&nbsp; ')\n" // preserve whitespaces for nicer output
-    "  \n"
-    "  msg = QgsMessageOutput.createMessageOutput()\n"
-    "  msg.setTitle('" + QObject::tr( "Python error" ).replace( "'", "\\'" ) + "')\n"
-    "  msg.setMessage(txt, QgsMessageOutput.MessageHtml)\n"
-    "  msg.showMessage()\n" );
-  runString(
-    "def qgis_except_hook(type, value, tb):\n"
-    "  qgis_except_hook_msg(type, value, tb, None)\n" );
-  runString(
-    "class QgisOutputCatcher:\n"
-    "  def __init__(self):\n"
-    "    self.data = ''\n"
-    "  def write(self, stuff):\n"
-    "    self.data += stuff\n"
-    "  def get_and_clean_data(self):\n"
-    "    tmp = self.data\n"
-    "    self.data = ''\n"
-    "    return tmp\n" );
+  // import QGIS utils
+  error_msg = QObject::tr( "Couldn't load QGIS utils." ) + "\n" + QObject::tr( "Python support will be disabled." );
+  if ( !runString("import qgis.utils", error_msg) )
+  {
+    exitPython();
+    return;
+  }
 
-  // hook for python console so all output will be redirected
-  // and then shown in console
-  runString(
-    "def console_display_hook(obj):\n"
-    "  __main__.__result = obj\n" );
-
-  installErrorHook();
-
   // initialize 'iface' object
-  runString( "iface = wrapinstance(" + QString::number(( unsigned long ) interface ) + ", QgisInterface)" );
-  runString( "plugins = {}" );
-
+  runString( "qgis.utils.initInterface(" + QString::number(( unsigned long ) interface ) + ")" );
 }
 
 void QgsPythonUtilsImpl::exitPython()
@@ -149,26 +113,22 @@
 
 void QgsPythonUtilsImpl::installErrorHook()
 {
-  runString( "sys.excepthook = qgis_except_hook" );
+  runString( "qgis.utils.installErrorHook()" );
 }
 
 void QgsPythonUtilsImpl::uninstallErrorHook()
 {
-  runString( "sys.excepthook = sys.__excepthook__" );
+  runString( "qgis.utils.uninstallErrorHook()" );
 }
 
 void QgsPythonUtilsImpl::installConsoleHooks()
 {
-  runString( "sys.displayhook = console_display_hook\n" );
-
-  runString( "_old_stdout = sys.stdout\n" );
-  runString( "sys.stdout = QgisOutputCatcher()\n" );
+  runString( "qgis.utils.installConsoleHooks()" );
 }
 
 void QgsPythonUtilsImpl::uninstallConsoleHooks()
 {
-  runString( "sys.displayhook = sys.__displayhook__" );
-  runString( "sys.stdout = _old_stdout" );
+  runString( "qgis.utils.uninstallConsoleHooks()" );
 }
 
 
@@ -193,6 +153,8 @@
     msgOnError = QObject::tr( "An error occured during execution of following code:" ) + "\n<tt>" + command + "</tt>";
   }
 
+  // TODO: use python implementation
+
   QString traceback = getTraceback();
   QString path, version;
   evalString( "str(sys.path)", path );
@@ -339,7 +301,7 @@
 
 QString QgsPythonUtilsImpl::getResult()
 {
-  return getVariableFromMain( "__result" );
+  return getVariableFromMain( "qgis.utils.console_output" );
 }
 
 QString QgsPythonUtilsImpl::PyObjectToQString( PyObject* obj )
@@ -425,6 +387,8 @@
 {
   PyObject* res = PyRun_String( command.toUtf8().data(), Py_eval_input, mMainDict, mMainDict );
 
+  // TODO: error handling
+
   if ( res != NULL )
   {
     result = PyObjectToQString( res );
@@ -472,106 +436,33 @@
 
 QString QgsPythonUtilsImpl::getPluginMetadata( QString pluginName, QString function )
 {
-  QString command = pluginName + "." + function + "()";
-  QString retval = "???";
-
-  // temporary disable error hook - UI will handle this gracefully
-  uninstallErrorHook();
-  PyObject* obj = PyRun_String( command.toUtf8().data(), Py_eval_input, mMainDict, mMainDict );
-
-  if ( PyErr_Occurred() )
-  {
-    PyErr_Print(); // just print it to console
-    PyErr_Clear();
-    retval = "__error__";
-  }
-  else if ( PyUnicode_Check( obj ) )
-  {
-    PyObject* utf8 = PyUnicode_AsUTF8String( obj );
-    if ( utf8 )
-      retval = QString::fromUtf8( PyString_AS_STRING( utf8 ) );
-    else
-      retval = "__error__";
-    Py_XDECREF( utf8 );
-  }
-  else if ( PyString_Check( obj ) )
-  {
-    retval = PyString_AS_STRING( obj );
-  }
-  else
-  {
-    // bad python return value
-    retval = "__error__";
-  }
-  Py_XDECREF( obj );
-
-  installErrorHook();
-  return retval;
+  QString res;
+  QString str = "qgis.utils.pluginMetadata('" + pluginName + "', '"+function+"')";
+  evalString(str, res);
+  //QgsDebugMsg("metadata "+pluginName+" - '"+function+"' = "+res);
+  return res;
 }
 
 
 bool QgsPythonUtilsImpl::loadPlugin( QString packageName )
 {
-  // load plugin's package and ensure that plugin is reloaded when changed
-  runString(
-    "try:\n"
-    "  import " + packageName + "\n"
-    "  __main__.__plugin_result = 'OK'\n"
-    "except:\n"
-    "  __main__.__plugin_result = 'ERROR'\n" );
-
-  if ( getVariableFromMain( "__plugin_result" ) == "OK" )
-    return true;
-
-  // snake in the grass, we know it's there
-  runString( "sys.path_importer_cache.clear()" );
-
-  // retry
-  runString(
-    "try:\n"
-    "  import " + packageName + "\n"
-    "  reload(" + packageName + ")\n"
-    "  __main__.__plugin_result = 'OK'\n"
-    "except:\n"
-    "  qgis_except_hook_msg(sys.exc_type, sys.exc_value, sys.exc_traceback, "
-    "'Couldn\\'t load plugin \"" + packageName + "\" from [\\'' + '\\', \\''.join(sys.path) + '\\']')\n"
-    "  __main__.__plugin_result = 'ERROR'\n" );
-
-  return getVariableFromMain( "__plugin_result" ) == "OK";
+  QString output;
+  evalString("qgis.utils.loadPlugin('" + packageName + "')", output);
+  return (output == "True");
 }
 
 
 bool QgsPythonUtilsImpl::startPlugin( QString packageName )
 {
-  QString pluginPythonVar = "plugins['" + packageName + "']";
-
-  QString errMsg = QObject::tr( "Couldn't load plugin %1" ).arg( packageName );
-
-  // create an instance of the plugin
-  if ( !runString( pluginPythonVar + " = " + packageName + ".classFactory(iface)",
-                   QObject::tr( "%1 due an error when calling its classFactory() method" ).arg( errMsg ) ) )
-    return false;
-
-  // initGui
-  if ( !runString( pluginPythonVar + ".initGui()",
-                   QObject::tr( "%1 due an error when calling its initGui() method" ).arg( errMsg ) ) )
-    return false;
-
-  return true;
+  QString output;
+  evalString("qgis.utils.startPlugin('" + packageName + "')", output);
+  return (output == "True");
 }
 
 
 bool QgsPythonUtilsImpl::unloadPlugin( QString packageName )
 {
-  // unload and delete plugin!
-  QString varName = "plugins['" + packageName + "']";
-
-  QString errMsg = QObject::tr( "Error while unloading plugin %1" ).arg( packageName );
-
-  if ( !runString( varName + ".unload()", errMsg ) )
-    return false;
-  if ( !runString( "del " + varName, errMsg ) )
-    return false;
-
-  return true;
+  QString output;
+  evalString("qgis.utils.unloadPlugin('" + packageName + "')", output);
+  return (output == "True");
 }



More information about the QGIS-commit mailing list