[rkward-cvs] SF.net SVN: rkward:[2488] trunk/rkward

tfry at users.sourceforge.net tfry at users.sourceforge.net
Tue May 19 13:38:00 UTC 2009


Revision: 2488
          http://rkward.svn.sourceforge.net/rkward/?rev=2488&view=rev
Author:   tfry
Date:     2009-05-19 13:38:00 +0000 (Tue, 19 May 2009)

Log Message:
-----------
Add rk.call.plugin()-function to invoke (and optionally submit) RKWard plugins from R code.
The main idea behind this is that this can be used to build automated plugin tests.

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/rkward/plugin/rkcomponent.cpp
    trunk/rkward/rkward/plugin/rkcomponent.h
    trunk/rkward/rkward/plugin/rkcomponentmap.cpp
    trunk/rkward/rkward/plugin/rkcomponentmap.h
    trunk/rkward/rkward/plugin/rkstandardcomponent.cpp
    trunk/rkward/rkward/plugin/rkstandardcomponent.h
    trunk/rkward/rkward/rbackend/rinterface.cpp
    trunk/rkward/rkward/rbackend/rpackages/rkward/R/public.R
    trunk/rkward/rkward/windows/rkhtmlwindow.cpp

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/ChangeLog	2009-05-19 13:38:00 UTC (rev 2488)
@@ -1,11 +1,12 @@
-- Fixed: Opening most file types (e.g. PDF) from the help browser was broken	TODO: backport in basic form
+- Fixed: Opening most file types (e.g. PDF) from the help browser was broken		TODO: output refresh is somewhat broken, now
 - Fixed: Make built-in editor work again for file.edit ()
 - (Almost) all plugins now write a header to the output window		TODO: work in progress; which ones should not?
 - Add convenience function "makeHeaderCode()" for use inside plugins	TODO: document, adjust for tests (->rk.describe.alternative())
 - Adjust some icons
+- Plugins can be invoked from R code (mostly for internal purposes)
 - Add "Run again" link for plugin generated output		TODO: revisit plugins without header
 - Fixed: All objects in .Globalenv would be revisited if a single object was added / removed		TODO: backport? (r2466, 2473)
-- Fixed: Screen device in rkward was not seen as interactive by R	TODO: backport (r2462)
+- Fixed: Screen device in rkward was not seen as interactive by R
 
 --- Version 0.5.0d - May-10-2009
 - Fixed: Container of detached windows would sometimes remain after reattaching

Modified: trunk/rkward/rkward/plugin/rkcomponent.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponent.cpp	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/plugin/rkcomponent.cpp	2009-05-19 13:38:00 UTC (rev 2488)
@@ -94,14 +94,13 @@
 	return out;
 }
 
-RKComponent::UnserializeError RKComponentBase::unserializeState (const QString &state) {
+RKComponent::UnserializeError RKComponentBase::unserializeState (const QStringList &state) {
 	RK_TRACE (PLUGIN);
 
 	QMap<QString, QString> props;
 
-	QStringList lines = state.split ('\n');
-	for (int i = 0; i < lines.count (); ++i) {
-		QString line = lines[i];
+	for (int i = 0; i < state.count (); ++i) {
+		QString line = state[i];
 		int sep = line.indexOf ('=');
 		if (sep < 0) return BadFormat;
 		props.insert (RKCommonFunctions::unescape (line.left (sep)), RKCommonFunctions::unescape (line.mid (sep+1)));

Modified: trunk/rkward/rkward/plugin/rkcomponent.h
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponent.h	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/plugin/rkcomponent.h	2009-05-19 13:38:00 UTC (rev 2488)
@@ -92,9 +92,9 @@
 
 /** serialize the state of this component / property and all its children. Note: Only the non-internal property-values are serialzed, not the components / properties themselves. @see fetchPropertyValuesRecursive() */
 	QString serializeState () const;
-/** set values from a string created with serializeState(). @see serializeState (), @see setPropertyValues ().
+/** set values from a string created with serializeState() (split by "\n"). @see serializeState (), @see setPropertyValues ().
 @returns status code */
-	UnserializeError unserializeState (const QString &state);
+	UnserializeError unserializeState (const QStringList &state);
 protected:
 	QHash<QString, RKComponentBase*> child_map;
 	bool required;

Modified: trunk/rkward/rkward/plugin/rkcomponentmap.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponentmap.cpp	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/plugin/rkcomponentmap.cpp	2009-05-19 13:38:00 UTC (rev 2488)
@@ -232,12 +232,15 @@
 }
 
 //static
-bool RKComponentMap::invokeComponent (const QString &component_id, const QString &serialized_settings, bool dosubmit) {
+bool RKComponentMap::invokeComponent (const QString &component_id, const QStringList &serialized_settings, ComponentInvocationMode submit_mode, QString *message) {
 	RK_TRACE (PLUGIN);
 
+	QString _message;
 	RKComponentHandle *handle = getComponentHandle (component_id);
 	if (!handle) {
-		KMessageBox::sorry (RKWardMainWindow::getMain (), i18n ("You tried to invoke a plugin called '%1', but that plugin is currently unknown. Probably you need to load the corresponding PluginMap (Settings->Configure RKWard->Plugins), or perhaps the plugin was renamed.").arg (component_id), i18n ("No such plugin"));
+		_message = i18n ("You tried to invoke a plugin called '%1', but that plugin is currently unknown. Probably you need to load the corresponding PluginMap (Settings->Configure RKWard->Plugins), or perhaps the plugin was renamed.").arg (component_id);
+		if (message) *message = _message;
+		else KMessageBox::sorry (RKWardMainWindow::getMain (), _message, i18n ("No such plugin"));
 		return false;
 	}
 
@@ -245,18 +248,34 @@
 	RK_ASSERT (component);
 
 	RKComponent::UnserializeError error = component->unserializeState (serialized_settings);
-	if (error == RKComponent::NoError) return true;
 	if (error == RKComponent::BadFormat) {
-		KMessageBox::error (component, i18n ("Bad serialization format while trying to invoke plugin '%1'. Please contact the RKWard team (Help->About RKWard->Authors).").arg (component_id), i18n ("Bad serialization format"));
+		_message = i18n ("Bad serialization format while trying to invoke plugin '%1'. Please contact the RKWard team (Help->About RKWard->Authors).").arg (component_id);
+		if (message) *message = _message;
+		else KMessageBox::error (component, _message, i18n ("Bad serialization format"));
 		return false;
 	}
 	if (error == RKComponent::NotAllSettingsApplied) {
-		KMessageBox::information (component, i18n ("Not all specified settings could be applied. Most likely this is because some R objects are no longer present in your current workspace."), i18n ("Not all settings applied"));
+		_message = i18n ("Not all specified settings could be applied. Most likely this is because some R objects are no longer present in your current workspace.");
+		if (message) *message = _message;
+		else KMessageBox::information (component, _message, i18n ("Not all settings applied"));
 		// TODO: Don't show again-box?
 		// not considered an error
 	}
 
-#warning TODO: support automatic submit
+	// Auto-Submit
+	if (submit_mode != ManualSubmit) {
+		// if the plugin takes longer than 5 seconds to settle, than that really is sort of buggy...
+		bool submit_ok = component->submit (5000);
+		if (submit_ok || (submit_mode == AutoSubmitOrFail)) component->close ();
+		if (!submit_ok) {
+			_message.append (i18n ("\nThe plugin could not be auto-submitted with these settings."));
+			if (message) *message = _message;
+			else KMessageBox::sorry (RKWardMainWindow::getMain (), _message, i18n ("Could not submit"));
+
+			return (submit_mode != AutoSubmitOrFail);
+		}
+	}
+
 	return true;
 }
 

Modified: trunk/rkward/rkward/plugin/rkcomponentmap.h
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponentmap.h	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/plugin/rkcomponentmap.h	2009-05-19 13:38:00 UTC (rev 2488)
@@ -169,8 +169,15 @@
 	static void initialize ();
 /** returns the context identified by id */
 	static RKContextMap *getContext (const QString &id);
-/** invokes the specified component as toplevel */
-	static bool invokeComponent (const QString &component_id, const QString &serialized_settings, bool dosubmit=false);
+
+	enum ComponentInvocationMode {
+		ManualSubmit,
+		AutoSubmit,
+		AutoSubmitOrFail
+	};
+/** invokes the specified component as toplevel
+ at param message If a non-null pointer to QString is given, error messages are written into this string *instead* of being displayed */
+	static bool invokeComponent (const QString &component_id, const QStringList &serialized_settings, ComponentInvocationMode submit_mode = ManualSubmit, QString *message=0);
 private:
 /** typedef for easy reference to iterator */
 	typedef QMap<QString, RKComponentHandle*> ComponentMap;

Modified: trunk/rkward/rkward/plugin/rkstandardcomponent.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkstandardcomponent.cpp	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/plugin/rkstandardcomponent.cpp	2009-05-19 13:38:00 UTC (rev 2488)
@@ -24,6 +24,7 @@
 #include <qtimer.h>
 #include <QVBoxLayout>
 #include <QGroupBox>
+#include <QTime>
 
 #include <klocale.h>
 #include <kmessagebox.h>
@@ -308,7 +309,34 @@
 	changed ();
 }
 
+bool RKStandardComponent::submit (int max_wait) {
+	RK_TRACE (PLUGIN);
 
+	QTime t;
+	t.start ();
+	while ((handle_change_timer->isActive () || backend->isBusy ()) && (t.elapsed () < max_wait)) {
+		QCoreApplication::processEvents (QEventLoop::ExcludeUserInputEvents, (max_wait / 2));
+	}
+	if (handle_change_timer->isActive () || backend->isBusy ()) {
+		return false;
+	}
+	if (isSatisfied ()) {
+		gui->ok ();
+		return true;
+	}
+	return false;
+}
+
+void RKStandardComponent::close () {
+	RK_TRACE (PLUGIN);
+
+	if (gui && (!parentComponent ())) {
+		QTimer::singleShot (0, gui, SLOT (close ()));
+	} else {
+		RK_ASSERT (false);
+	}
+}
+
 void RKStandardComponent::changed () {
 	RK_TRACE (PLUGIN);
 

Modified: trunk/rkward/rkward/plugin/rkstandardcomponent.h
===================================================================
--- trunk/rkward/rkward/plugin/rkstandardcomponent.h	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/plugin/rkstandardcomponent.h	2009-05-19 13:38:00 UTC (rev 2488)
@@ -2,7 +2,7 @@
                           rkstandardcomponent  -  description
                              -------------------
     begin                : Sun Feb 19 2006
-    copyright            : (C) 2006, 2007 by Thomas Friedrichsmeier
+    copyright            : (C) 2006, 2007, 2009 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -68,6 +68,12 @@
 	QString getFilename () { return filename; };
 	RKComponentHandle *getHandle () { return handle; };
 	bool haveHelp () { return have_help; };
+/** tries to submit. Warning: This function waits for all changes to come in and may not return immediately!
+ at param max_wait Maximum time to wait for changes to settle in msecs (approx.)
+ at return true, if the plugin-code could be submitted */
+	bool submit (int max_wait=1000);
+/** convenience access function: closes the corresponding GUI */
+	void close ();
 public slots:
 /** this gets called by the script-backend, when it's done. Might enable the
 	submit button or destruct the plugin. */

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2009-05-19 13:38:00 UTC (rev 2488)
@@ -32,6 +32,8 @@
 #include "../windows/rcontrolwindow.h"
 #include "../windows/rkworkplace.h"
 #include "../windows/rkcommandlog.h"
+#include "../plugin/rkcomponentmap.h"
+#include "../misc/rkcommonfunctions.h"
 
 #include "../windows/rkwindowcatcher.h"
 
@@ -373,6 +375,25 @@
 		if (res != KMessageBox::Continue) {
 			issueCommand (".rk.set.reply (FALSE)", RCommand::App | RCommand::Sync, QString::null, 0, 0, request->in_chain);
 		}
+	} else if (call == "doPlugin") {
+		if (request->call.count () >= 3) {
+			QString message;
+			bool ok;
+			RKComponentMap::ComponentInvocationMode mode = RKComponentMap::ManualSubmit;
+			if (request->call[2] == "auto") mode = RKComponentMap::AutoSubmit;
+			else if (request->call[2] == "test") mode = RKComponentMap::AutoSubmitOrFail;
+			ok = RKComponentMap::invokeComponent (request->call[1], request->call.mid (3), mode, &message);
+
+			if (message.isEmpty ()) {
+				issueCommand (".rk.set.reply (NULL)", RCommand::App | RCommand::Sync, QString::null, 0, 0, request->in_chain);
+			} else {
+				QString type = "warning";
+				if (!ok) type = "error";
+				issueCommand (".rk.set.reply (list (type=\"" + type + "\", message=\"" + RKCommonFunctions::escape (message) + "\"))", RCommand::App | RCommand::Sync, QString::null, 0, 0, request->in_chain);
+			}
+		} else {
+			RK_ASSERT (false);
+		}
 	} else {
 		issueCommand (".rk.set.reply (\"Unrecognized call '" + call + "'. Ignoring\")", RCommand::App | RCommand::Sync, QString::null, 0, 0, request->in_chain);
 	}

Modified: trunk/rkward/rkward/rbackend/rpackages/rkward/R/public.R
===================================================================
--- trunk/rkward/rkward/rbackend/rpackages/rkward/R/public.R	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/rbackend/rpackages/rkward/R/public.R	2009-05-19 13:38:00 UTC (rev 2488)
@@ -318,3 +318,28 @@
 	}
 	.Call ("rk.edit.files", file, title, name)
 }
+
+"rk.call.plugin" <- function (plugin, ..., submit.mode = c ("manual", "auto", "test")) {
+	# prepare arguments
+	settings <- list (...)
+	callstrings <- list ()
+	callstrings[1] <- plugin
+	callstrings[2] <- match.arg (submit.mode)
+	if (length (settings) > 0) {
+		for (i in 1:length(settings)) {
+			callstrings[i+2] = paste (names(settings)[i], settings[i], sep="=")
+		}
+	}
+
+	# do call
+	res <- .rk.do.call ("doPlugin", callstrings)
+
+	# handle result
+	if (!is.null (res)) {
+		if (res$type == "warning") {
+			warning (res$message)
+		} else {
+			stop (res$message)
+		}
+	}
+}

Modified: trunk/rkward/rkward/windows/rkhtmlwindow.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkhtmlwindow.cpp	2009-05-18 16:48:13 UTC (rev 2487)
+++ trunk/rkward/rkward/windows/rkhtmlwindow.cpp	2009-05-19 13:38:00 UTC (rev 2488)
@@ -222,8 +222,7 @@
 			QString path = url.path ();
 			if (path.startsWith ('/')) path = path.mid (1);
 			int sep = path.indexOf ('/');
-			RKComponentMap::invokeComponent (path.left (sep), path.mid (sep+1));
-#warning TODO: error handling!
+			RKComponentMap::invokeComponent (path.left (sep), path.mid (sep+1).split ('\n'));
 			return true;
 		} else {
 			bool ok = false;


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the rkward-tracker mailing list