Skip to content

Commit

Permalink
[FEATURE] Add support to user-defined python macros into project file:
Browse files Browse the repository at this point in the history
macros are executed when a project is loaded (openProject function), saved (saveProject function) or is closing (closeProject function),
macros' python code is embedded in the project file (XML),
a bar (non-blocking) is shown on the top of the canvas to ask to the user whether enable project macros.

Work done for ARPA Piemonte
  • Loading branch information
brushtyler committed Aug 21, 2012
1 parent 953c4e0 commit 382187b
Show file tree
Hide file tree
Showing 9 changed files with 510 additions and 21 deletions.
51 changes: 51 additions & 0 deletions python/utils.py
Expand Up @@ -296,6 +296,57 @@ def pluginDirectory(packageName):
""" return directory where the plugin resides. Plugin must be loaded already """
return os.path.dirname(sys.modules[packageName].__file__)

def reloadProjectMacros():
# unload old macros
unloadProjectMacros()

from qgis.core import QgsProject
code, ok = QgsProject.instance().readEntry("Macros", "/pythonCode")
if not ok or code.isEmpty():
return

# create a new empty python module
import imp
mod = imp.new_module("proj_macros_mod")

# set the module code and store it sys.modules
exec unicode(code) in mod.__dict__
sys.modules["proj_macros_mod"] = mod

# load new macros
openProjectMacro()

def unloadProjectMacros():
if "proj_macros_mod" not in sys.modules:
return
# unload old macros
closeProjectMacro()
# destroy the reference to the module
del sys.modules["proj_macros_mod"]


def openProjectMacro():
if "proj_macros_mod" not in sys.modules:
return
mod = sys.modules["proj_macros_mod"]
if hasattr(mod, 'openProject'):
mod.openProject()

def saveProjectMacro():
if "proj_macros_mod" not in sys.modules:
return
mod = sys.modules["proj_macros_mod"]
if hasattr(mod, 'saveProject'):
mod.saveProject()

def closeProjectMacro():
if "proj_macros_mod" not in sys.modules:
return
mod = sys.modules["proj_macros_mod"]
if hasattr(mod, 'closeProject'):
mod.closeProject()


#######################
# IMPORT wrapper

Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -85,6 +85,7 @@ SET(QGIS_APP_SRCS
qgsmeasuredialog.cpp
qgsmeasuretool.cpp
qgsmergeattributesdialog.cpp
qgsmessagebar.cpp
qgsoptions.cpp
qgspastetransformations.cpp
qgspointrotationitem.cpp
Expand Down Expand Up @@ -232,6 +233,7 @@ SET (QGIS_APP_MOC_HDRS
qgsmeasuredialog.h
qgsmeasuretool.h
qgsmergeattributesdialog.h
qgsmessagebar.h
qgsoptions.h
qgspastetransformations.h
qgspluginmanager.h
Expand Down
96 changes: 79 additions & 17 deletions src/app/qgisapp.cpp
Expand Up @@ -139,6 +139,7 @@
#include "qgsmaptip.h"
#include "qgsmergeattributesdialog.h"
#include "qgsmessageviewer.h"
#include "qgsmessagebar.h"
#include "qgsmimedatautils.h"
#include "qgsmessagelog.h"
#include "qgsmultibandcolorrenderer.h"
Expand Down Expand Up @@ -454,19 +455,42 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
QSettings settings;
setFontSize( settings.value( "/fontPointSize", QGIS_DEFAULT_FONTSIZE ).toInt() );

QWidget *centralWidget = this->centralWidget();
QGridLayout *centralLayout = new QGridLayout( centralWidget );

This comment has been minimized.

Copy link
@NathanW2

NathanW2 Aug 21, 2012

Just need to reduce the grid layout margins to 0 to fix the border issue.

centralWidget->setLayout( centralLayout );

// "theMapCanvas" used to find this canonical instance later
mMapCanvas = new QgsMapCanvas( this, "theMapCanvas" );
mMapCanvas = new QgsMapCanvas( centralWidget, "theMapCanvas" );
mMapCanvas->setWhatsThis( tr( "Map canvas. This is where raster and vector "
"layers are displayed when added to the map" ) );

// set canvas color right away
int myRed = settings.value( "/qgis/default_canvas_color_red", 255 ).toInt();
int myGreen = settings.value( "/qgis/default_canvas_color_green", 255 ).toInt();
int myBlue = settings.value( "/qgis/default_canvas_color_blue", 255 ).toInt();
mMapCanvas->setCanvasColor( QColor( myRed, myGreen, myBlue ) );
setCentralWidget( mMapCanvas );

centralLayout->addWidget( mMapCanvas, 0, 0, 2, 1 );

//set the focus to the map canvas
mMapCanvas->setFocus();

// a bar to warn the user with non-blocking messages
mInfoBar = new QgsMessageBar( centralWidget );
mInfoBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
centralLayout->addWidget( mInfoBar, 0, 0, 1, 1 );

mMacrosWarn = QgsMessageBar::createMessage( tr( "Security warning:" ),
tr( "macros have been disabled." ),
QgsApplication::getThemeIcon( "/mIconWarn.png" ),
mInfoBar );

QToolButton *btnEnableMacros = new QToolButton( mMacrosWarn );
btnEnableMacros->setText( tr( "Enable" ) );
connect( btnEnableMacros, SIGNAL( clicked() ), mInfoBar, SLOT( popWidget() ) );
connect( btnEnableMacros, SIGNAL( clicked() ), this, SLOT( enableProjectMacros() ) );
mMacrosWarn->layout()->addWidget( btnEnableMacros );

// "theMapLegend" used to find this canonical instance later
mMapLegend = new QgsLegend( mMapCanvas, this, "theMapLegend" );

Expand Down Expand Up @@ -627,6 +651,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
QgsMessageLog::logMessage( tr( "QGIS Ready!" ) );

mMapTipsVisible = false;
mTrustedMacros = false;

// setup drag drop
setAcceptDrops( true );
Expand Down Expand Up @@ -2942,10 +2967,7 @@ void QgisApp::fileExit()

if ( saveDirty() )
{
deletePrintComposers();
removeAnnotationItems();
mMapCanvas->freeze( true );
removeAllLayers();
closeProject();
qApp->exit( 0 );
}
}
Expand Down Expand Up @@ -2992,11 +3014,7 @@ void QgisApp::fileNew( bool thePromptToSaveFlag, bool forceBlank )
}
}

deletePrintComposers();
removeAnnotationItems();

mMapCanvas->freeze( true );
removeAllLayers();
closeProject();
mMapCanvas->clear();

QgsProject* prj = QgsProject::instance();
Expand Down Expand Up @@ -3178,6 +3196,8 @@ void QgisApp::fileOpen()
return;
}

closeProject();

// Fix by Tim - getting the dirPath from the dialog
// directly truncates the last node in the dir path.
// This is a workaround for that
Expand All @@ -3186,12 +3206,6 @@ void QgisApp::fileOpen()
// Persist last used project dir
settings.setValue( "/UI/lastProjectDir", myPath );

deletePrintComposers();
removeAnnotationItems();
// clear out any stuff from previous project
mMapCanvas->freeze( true );
removeAllLayers();

QgsProject::instance()->setFileName( fullPath );

if ( ! QgsProject::instance()->read() )
Expand All @@ -3212,6 +3226,19 @@ void QgisApp::fileOpen()
mScaleEdit->updateScales( QgsProject::instance()->readListEntry( "Scales", "/ScalesList" ) );
}

// does the project have any macros?
if ( mPythonUtils && mPythonUtils->isEnabled() )
{
if ( settings.value( "/qgis/enable_macros", false ).toBool() )
{
enableProjectMacros();
}
else
{
mInfoBar->pushWidget( mMacrosWarn, 2 );
}
}

emit projectRead(); // let plug-ins know that we've read in a new
// project so that they can check any project
// specific plug-in state
Expand All @@ -3224,6 +3251,14 @@ void QgisApp::fileOpen()
}
} // QgisApp::fileOpen

void QgisApp::enableProjectMacros()
{
mTrustedMacros = true;

// load macros
QgsPythonRunner::run( "qgis.utils.reloadProjectMacros()" );
}


/**
adds a saved project to qgis, usually called on startup by specifying a
Expand Down Expand Up @@ -3363,6 +3398,13 @@ bool QgisApp::fileSave()
QgsProject::instance()->error() );
return false;
}

// run the saved project macro
if ( mTrustedMacros )
{
QgsPythonRunner::run( "qgis.utils.saveProjectMacro();" );
}

return true;
} // QgisApp::fileSave

Expand Down Expand Up @@ -5853,6 +5895,26 @@ bool QgisApp::saveDirty()
return answer != QMessageBox::Cancel;
} // QgisApp::saveDirty()

void QgisApp::closeProject()
{
// unload the project macros before changing anything
if ( mTrustedMacros )
{
QgsPythonRunner::run( "qgis.utils.unloadProjectMacros();" );
}

// remove the warning message from the bar if present
mInfoBar->popWidget( mMacrosWarn );

mTrustedMacros = false;

deletePrintComposers();
removeAnnotationItems();
// clear out any stuff from project
mMapCanvas->freeze( true );
removeAllLayers();
}


void QgisApp::changeEvent( QEvent* event )
{
Expand Down
15 changes: 14 additions & 1 deletion src/app/qgisapp.h
Expand Up @@ -71,6 +71,7 @@ class QgsGPSInformationWidget;
class QgsDecorationItem;

class QgsMessageLogViewer;
class QgsMessageBar;

class QgsScaleComboBox;

Expand Down Expand Up @@ -888,6 +889,12 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
void renderDecorationItems( QPainter *p );
void projectReadDecorationItems( );

//! clear out any stuff from project
void closeProject();

//! trust and load project macros
void enableProjectMacros();

signals:
/** emitted when a key is pressed and we want non widget sublasses to be able
to pick up on this (e.g. maplayer) */
Expand Down Expand Up @@ -972,7 +979,6 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
/**Deletes all the composer objects and clears mPrintComposers*/
void deletePrintComposers();


void saveAsVectorFileGeneral( bool saveOnlySelection );

/**Returns all annotation items in the canvas*/
Expand Down Expand Up @@ -1228,6 +1234,13 @@ class QgisApp : public QMainWindow, private Ui::MainWindow

QString mOldScale;

//! the user has trusted the project macros
bool mTrustedMacros;

//! a bar to display warnings in a non-blocker manner
QgsMessageBar *mInfoBar;
QWidget *mMacrosWarn;

#ifdef HAVE_TOUCH
bool gestureEvent( QGestureEvent *event );
void tapAndHoldTriggered( QTapAndHoldGesture *gesture );
Expand Down

0 comments on commit 382187b

Please sign in to comment.