[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