From 2b9d76105b2c313c8bdb464c097413b7bd480385 Mon Sep 17 00:00:00 2001
From: Juraj Fedel <wtxnh-scribus@yahoo.com.au>
Date: Sun, 31 Aug 2014 15:56:42 +0200
Subject: [PATCH] All in one: Run python script from CLI

Add the '--python-script file' CLI option to run 'file' as python
script.

Also enable the use of --no-gui CLI option
This option is useful to run a python script and then exit scribus with:
scribus --no-gui --python-script myscript.py
if it is run as:
scribus --python-script myscript.py
then after myscript.py finishes, the GUI is started.

I moved signal appStarted() from scribuscore to scribusapp,
it seems to be better place.
---
 scribus/main_nix.cpp                          |    8 ++---
 scribus/main_win32.cpp                        |    6 ++--
 scribus/plugins/scriptplugin/scriptercore.cpp |   29 +++++++++++-----
 scribus/plugins/scriptplugin/scriptercore.h   |    1 +
 scribus/scribusapp.cpp                        |   46 +++++++++++++++++++-----
 scribus/scribusapp.h                          |    4 ++
 scribus/scribuscore.cpp                       |    5 ---
 scribus/scribuscore.h                         |    5 ---
 8 files changed, 67 insertions(+), 37 deletions(-)

diff --git a/scribus/main_nix.cpp b/scribus/main_nix.cpp
index 8132b43..56156a1 100644
--- a/scribus/main_nix.cpp
+++ b/scribus/main_nix.cpp
@@ -78,13 +78,11 @@ int mainApp(int argc, char **argv)
 #endif // QT_VERSION == 0x040400
 #endif // Q_OS_UNIX
 	app.parseCommandLine();
+	int appRetVal=app.init();
+	if (appRetVal==EXIT_FAILURE)
+		return(EXIT_FAILURE);
 	if (app.useGUI)
-	{
-		int appRetVal=app.init();
-		if (appRetVal==EXIT_FAILURE)
-			return(EXIT_FAILURE);
 		return app.exec();
-	}
 	return EXIT_SUCCESS;	
 }
 
diff --git a/scribus/main_win32.cpp b/scribus/main_win32.cpp
index 9dccfa1..5ea48bc 100644
--- a/scribus/main_win32.cpp
+++ b/scribus/main_win32.cpp
@@ -114,10 +114,10 @@ int mainApp(ScribusQApp& app)
 	{
 #endif
 		app.parseCommandLine();
-		if (app.useGUI)
+		appRetVal = app.init();
+		if (appRetVal != EXIT_FAILURE)
 		{
-			appRetVal = app.init();
-			if (appRetVal != EXIT_FAILURE)
+			if (app.useGUI)
 				appRetVal = app.exec();
 		}
 #ifndef _DEBUG
diff --git a/scribus/plugins/scriptplugin/scriptercore.cpp b/scribus/plugins/scriptplugin/scriptercore.cpp
index 797f0ad..ac98839 100644
--- a/scribus/plugins/scriptplugin/scriptercore.cpp
+++ b/scribus/plugins/scriptplugin/scriptercore.cpp
@@ -36,6 +36,7 @@ for which a new license (GPL+exception) is in place.
 #include "prefscontext.h"
 #include "prefstable.h"
 #include "prefsmanager.h"
+#include "scribusapp.h" // need it to acces ScQApp->pythonScript
 
 ScripterCore::ScripterCore(QWidget* parent)
 {
@@ -66,6 +67,8 @@ ScripterCore::ScripterCore(QWidget* parent)
 
 	QObject::connect(pcon, SIGNAL(runCommand()), this, SLOT(slotExecute()));
 	QObject::connect(pcon, SIGNAL(paletteShown(bool)), this, SLOT(slotInteractiveScript(bool)));
+
+	QObject::connect(ScQApp, SIGNAL(appStarted()) , this, SLOT(slotRunPythonScript()) );
 }
 
 ScripterCore::~ScripterCore()
@@ -177,7 +180,7 @@ void ScripterCore::FinishScriptRun()
 void ScripterCore::runScriptDialog()
 {
 	QString fileName;
-	QString curDirPath = QDir::currentPath();
+	// QString curDirPath = QDir::currentPath();
 	RunScriptDialog dia( ScCore->primaryMainWindow(), m_enableExtPython );
 	if (dia.exec())
 	{
@@ -193,7 +196,7 @@ void ScripterCore::runScriptDialog()
 		}
 		rebuildRecentScriptsMenu();
 	}
-	QDir::setCurrent(curDirPath);
+	// QDir::setCurrent(curDirPath);
 	FinishScriptRun();
 }
 
@@ -247,7 +250,7 @@ void ScripterCore::slotRunScriptFile(QString fileName, bool inMainInterpreter)
 		global_state = PyThreadState_Get();
 		state = Py_NewInterpreter();
 		// Chdir to the dir the script is in
-		QDir::setCurrent(fi.absolutePath());
+		// QDir::setCurrent(fi.absolutePath());
 		// Init the scripter module in the sub-interpreter
 		initscribus(ScCore->primaryMainWindow());
 	}
@@ -292,11 +295,9 @@ void ScripterCore::slotRunScriptFile(QString fileName, bool inMainInterpreter)
 		// into a StringIO buffer for later extraction.
 		cm        += QString("except:\n");
 		cm        += QString("    import traceback\n");
-		cm        += QString("    import scribus\n");                  // we stash our working vars here
-		cm        += QString("    scribus._f=cStringIO.StringIO()\n");
-		cm        += QString("    traceback.print_exc(file=scribus._f)\n");
-		cm        += QString("    _errorMsg = scribus._f.getvalue()\n");
-		cm        += QString("    del(scribus._f)\n");
+		cm        += QString("    _errorMsg = traceback.format_exc()\n");
+		if (!ScCore->usingGUI())
+			cm += QString("    traceback.print_exc()\n");
 		// We re-raise the exception so the return value of PyRun_StringFlags reflects
 		// the fact that an exception has ocurred.
 		cm        += QString("    raise\n");
@@ -321,7 +322,7 @@ void ScripterCore::slotRunScriptFile(QString fileName, bool inMainInterpreter)
 				qDebug("Exception was:");
 				PyErr_Print();
 			}
-			else
+			else if (ScCore->usingGUI())
 			{
 				QString errorMsg = PyString_AsString(errorMsgPyStr);
 				// Display a dialog to the user with the exception
@@ -353,6 +354,16 @@ void ScripterCore::slotRunScriptFile(QString fileName, bool inMainInterpreter)
 	enableMainWindowMenu();
 }
 
+// needed for running script from CLI - this is activated by signal ScribusQApp::appStarted()
+void ScripterCore::slotRunPythonScript()
+{
+	if (!ScQApp->pythonScript.isNull())
+	{
+		slotRunScriptFile(ScQApp->pythonScript, true);
+		FinishScriptRun();
+	}
+}
+
 void ScripterCore::slotRunScript(const QString Script)
 {
 	// Prevent two scripts to be run concurrently or face crash!
diff --git a/scribus/plugins/scriptplugin/scriptercore.h b/scribus/plugins/scriptplugin/scriptercore.h
index ff1e001..1e929f4 100644
--- a/scribus/plugins/scriptplugin/scriptercore.h
+++ b/scribus/plugins/scriptplugin/scriptercore.h
@@ -39,6 +39,7 @@ public slots:
 	void StdScript(QString filebasename);
 	void RecentScript(QString fn);
 	void slotRunScriptFile(QString fileName, bool inMainInterpreter = false);
+	void slotRunPythonScript(); // needed for running python script from CLI
 	void slotRunScript(const QString Script);
 	void slotInteractiveScript(bool);
 	void slotExecute();
diff --git a/scribus/scribusapp.cpp b/scribus/scribusapp.cpp
index 98e397c..fc56dbd 100644
--- a/scribus/scribusapp.cpp
+++ b/scribus/scribusapp.cpp
@@ -66,6 +66,7 @@ for which a new license (GPL+exception) is in place.
 #define ARG_PREFS "--prefs"
 #define ARG_UPGRADECHECK "--upgradecheck"
 #define ARG_TESTS "--tests"
+#define ARG_PYTHONSCRIPT "--python-script"
 
 #define ARG_VERSION_SHORT "-v"
 #define ARG_HELP_SHORT "-h"
@@ -81,6 +82,7 @@ for which a new license (GPL+exception) is in place.
 #define ARG_PREFS_SHORT "-pr"
 #define ARG_UPGRADECHECK_SHORT "-u"
 #define ARG_TESTS_SHORT "-T"
+#define ARG_PYTHONSCRIPT_SHORT "-py"
 
 // Qt wants -display not --display or -d
 #define ARG_DISPLAY_QT "-display"
@@ -204,10 +206,9 @@ void ScribusQApp::parseCommandLine()
 		uc.fetch();
 	}
 	//Dont run the GUI init process called from main.cpp, and return
-	if (!header)
-		useGUI=true;
-	else
-		return;
+	if (header)
+		std::exit(EXIT_SUCCESS);
+	useGUI = true;
 	//We are going to run something other than command line help
 	for(int i = 1; i < argsc; i++) {
 		arg = args[i];
@@ -244,14 +245,27 @@ void ScribusQApp::parseCommandLine()
 					std::cout << tr("File %1 does not exist, aborting.").arg(prefsUserFile).toLocal8Bit().data() << std::endl;
 				}
 				showUsage();
-				useGUI=false;
-				return;
+				std::exit(EXIT_FAILURE);
 			} else {
 				++i;
 			}
 		} else if (strncmp(arg.toLocal8Bit().data(),"-psn_",4) == 0)
 		{
 			// Andreas Vox: Qt/Mac has -psn_blah flags that must be accepted.
+		} else if (arg == ARG_PYTHONSCRIPT || arg == ARG_PYTHONSCRIPT_SHORT) {
+			pythonScript = QFile::decodeName(args[i + 1].toLocal8Bit());
+			if (!QFileInfo(pythonScript).exists()) {
+				showHeader();
+				if (pythonScript.left(1) == "-" || pythonScript.left(2) == "--") {
+					std::cout << tr("Invalid argument: ").toLocal8Bit().data() << pythonScript.toLocal8Bit().data() << std::endl;
+				} else {
+					std::cout << tr("File %1 does not exist, aborting.").arg(pythonScript).toLocal8Bit().data() << std::endl;
+				}
+				showUsage();
+				std::exit(EXIT_FAILURE);
+			} else {
+				++i;
+			}
 		} else {
 			fileName = QFile::decodeName(args[i].toLocal8Bit());
 			if (!QFileInfo(fileName).exists()) {
@@ -262,8 +276,7 @@ void ScribusQApp::parseCommandLine()
 					std::cout << tr("File %1 does not exist, aborting.").arg(fileName).toLocal8Bit().data() << std::endl;
 				}
 				showUsage();
-				useGUI=false;
-				return;
+				std::exit(EXIT_FAILURE);
 			}
 			else
 			{
@@ -283,8 +296,20 @@ int ScribusQApp::init()
 	processEvents();
 	ScCore->init(useGUI, swapDialogButtonOrder, filesToLoad);
 	int retVal=EXIT_SUCCESS;
-	if (useGUI)
+	/* TODO:
+	 * When Scribus is truly able to run without GUI
+	 * we should uncomment if (useGUI)
+	 * and delete if (true)
+	 */
+	// if (useGUI)
+	if (true)
 		retVal=ScCore->startGUI(showSplash, showFontInfo, showProfileInfo, lang, prefsUserFile);
+
+	// A hook for plugins and scripts to trigger on. Some plugins and scripts
+	// require the app to be fully set up (in particular, the main window to be
+	// built and shown) before running their setup.
+	emit appStarted();
+
 	return retVal;
 }
 
@@ -464,7 +489,8 @@ void ScribusQApp::showUsage()
 	printArgLine(ts, ARG_SWAPDIABUTTONS_SHORT, ARG_SWAPDIABUTTONS, tr("Use right to left dialog button ordering (eg. Cancel/No/Yes instead of Yes/No/Cancel)") );
 	printArgLine(ts, ARG_UPGRADECHECK_SHORT, ARG_UPGRADECHECK, tr("Download a file from the Scribus website and show the latest available version.") );
 	printArgLine(ts, ARG_VERSION_SHORT, ARG_VERSION, tr("Output version information and exit") );
-	
+	printArgLine(ts, ARG_PYTHONSCRIPT_SHORT, QString(QString(ARG_PYTHONSCRIPT) + QString(" ") + tr("filename")).toLocal8Bit().constData(), tr("Run filename in Python scripter") );
+	printArgLine(ts, ARG_NOGUI_SHORT, ARG_NOGUI, tr("Do not start GUI") );
 	
 #if defined(_WIN32) && !defined(_CONSOLE)
 	printArgLine(ts, ARG_CONSOLE_SHORT, ARG_CONSOLE, tr("Display a console window") );
diff --git a/scribus/scribusapp.h b/scribus/scribusapp.h
index fe27092..e2f78a1 100644
--- a/scribus/scribusapp.h
+++ b/scribus/scribusapp.h
@@ -69,6 +69,7 @@ class SCRIBUS_API ScribusQApp : public QApplication
 		bool neverSplashExists();
 		const QString& currGUILanguage() { return GUILang; }
 		ScDLManager* dlManager() { return m_scDLMgr; }
+		QString pythonScript; // script to be run in python from CLI
 
 	private:
 		ScribusCore* m_ScCore;
@@ -105,6 +106,9 @@ class SCRIBUS_API ScribusQApp : public QApplication
 
 	protected slots:
 		void downloadComplete(const QString& t);
+
+	signals:
+		void appStarted();
 };
 
 #endif
diff --git a/scribus/scribuscore.cpp b/scribus/scribuscore.cpp
index 292406b..a275161 100644
--- a/scribus/scribuscore.cpp
+++ b/scribus/scribuscore.cpp
@@ -140,11 +140,6 @@ int ScribusCore::startGUI(bool showSplash, bool showFontInfo, bool showProfileIn
 	{
 		scribus->slotRaiseOnlineHelp();
 	}
-
-	// A hook for plugins and scripts to trigger on. Some plugins and scripts
-	// require the app to be fully set up (in particular, the main window to be
-	// built and shown) before running their setup.
-	emit appStarted();
 	
 	return EXIT_SUCCESS;
 }
diff --git a/scribus/scribuscore.h b/scribus/scribuscore.h
index ce0e6ef..91f5eb2 100644
--- a/scribus/scribuscore.h
+++ b/scribus/scribuscore.h
@@ -142,11 +142,6 @@ protected:
 	bool m_HaveGS;
 	bool m_HavePngAlpha;
 	bool m_HaveTiffSep;
-	
-	
-signals:
-	void appStarted();
-
 };
 
 /*
-- 
1.7.2.3

