[rkward/work/generalized_preview] rkward: First draft of the R support functions for data and custom previews.

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Sun Dec 13 20:03:21 UTC 2015


Git commit f434a6c09406cb783f1798adb96566f4ba0d07b9 by Thomas Friedrichsmeier.
Committed on 13/12/2015 at 20:01.
Pushed by tfry into branch 'work/generalized_preview'.

First draft of the R support functions for data and custom previews.

The idea is that the preview box will operate in three different modes:
"plot", as before. "data" for a simple way to send preview data through rk.edi(),
and "custom" to cover all other needs.

Frontend side is not yet done.

M  +24   -16   rkward/plugin/rkpreviewbox.cpp
M  +9    -4    rkward/plugin/rkpreviewbox.h
M  +0    -19   rkward/rbackend/rpackages/rkward/R/internal.R
M  +3    -2    rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
M  +72   -0    rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R

http://commits.kde.org/rkward/f434a6c09406cb783f1798adb96566f4ba0d07b9

diff --git a/rkward/plugin/rkpreviewbox.cpp b/rkward/plugin/rkpreviewbox.cpp
index db98265..7bf9731 100644
--- a/rkward/plugin/rkpreviewbox.cpp
+++ b/rkward/plugin/rkpreviewbox.cpp
@@ -37,20 +37,20 @@ RKPreviewBox::RKPreviewBox (const QDomElement &element, RKComponent *parent_comp
 	RK_TRACE (PLUGIN);
 
 	preview_active = false;
-	last_plot_done = true;
-	new_plot_pending = false;
+	prior_preview_done = true;
+	new_preview_pending = false;
 	dev_num = 0;
 
 	// get xml-helper
 	XMLHelper *xml = parent_component->xmlHelper ();
 
+	preview_mode = (PreviewMode) xml->getMultiChoiceAttribute (element, "mode", "plot;data;custom", 0);
+	idprop = RObject::rQuote (QString ().sprintf ("%p", this));
+
 	// create and add property
 	addChild ("state", state = new RKComponentPropertyBool (this, true, preview_active, "active", "inactive"));
 	state->setInternal (true);	// restoring this does not make sense.
 	connect (state, SIGNAL (valueChanged(RKComponentPropertyBase*)), this, SLOT (changedState(RKComponentPropertyBase*)));
-	idprop = new RKComponentPropertyBase (this, false);
-	idprop->setValue (RObject::rQuote (QString ().sprintf ("%p", this)));
-	addChild ("id", idprop);
 
 	// create checkbox
 	QVBoxLayout *vbox = new QVBoxLayout (this);
@@ -89,6 +89,14 @@ RKPreviewBox::~RKPreviewBox () {
 	killPreview ();
 }
 
+QVariant RKPreviewBox::value(const QString& modifier) {
+	if (modifier == "id") {
+		return idprop;
+	}
+	return (state->value (modifier));
+}
+
+
 void RKPreviewBox::changedState (RKComponentPropertyBase *) {
 	RK_TRACE (PLUGIN);
 
@@ -141,19 +149,19 @@ void RKPreviewBox::tryPreviewNow () {
 		return;
 	}
 
-	if (!last_plot_done) {		// if the last plot is not done, yet, wait before starting the next.
-		new_plot_pending = true;
+	if (!prior_preview_done) {		// if the last plot is not done, yet, wait before starting the next.
+		new_preview_pending = true;
 		updateStatusLabel ();
 		return;
 	}
 
 	preview_active = true;
-	RKGlobals::rInterface ()->issueCommand (".rk.startPreviewDevice (" + idprop->value ().toString () + ')', RCommand::Plugin | RCommand::Sync | RCommand::GetIntVector, QString (), this, START_DEVICE);
+	RKGlobals::rInterface ()->issueCommand (".rk.startPreviewDevice (" + idprop + ')', RCommand::Plugin | RCommand::Sync | RCommand::GetIntVector, QString (), this, START_DEVICE);
 	RKCaughtX11Window::setStatusMessage (dev_num, i18n ("Preview updating"));
 	RKGlobals::rInterface ()->issueCommand ("local({\n" + code_property->preview () + "})\n", RCommand::Plugin | RCommand::Sync, QString (), this, DO_PLOT);
 
-	last_plot_done = false;
-	new_plot_pending = false;
+	prior_preview_done = false;
+	new_preview_pending = false;
 
 	updateStatusLabel ();
 }
@@ -163,17 +171,17 @@ void RKPreviewBox::killPreview () {
 
 	if (!preview_active) return;
 	preview_active = false;
-	RKGlobals::rInterface ()->issueCommand (".rk.killPreviewDevice (" + idprop->value ().toString () + ')', RCommand::Plugin | RCommand::Sync);
+	RKGlobals::rInterface ()->issueCommand (".rk.killPreviewDevice (" + idprop + ')', RCommand::Plugin | RCommand::Sync);
 
-	last_plot_done = true;
-	new_plot_pending = false;
+	prior_preview_done = true;
+	new_preview_pending = false;
 }
 
 void RKPreviewBox::rCommandDone (RCommand *command) {
 	RK_TRACE (PLUGIN);
 
-	last_plot_done = true;
-	if (new_plot_pending) tryPreview ();
+	prior_preview_done = true;
+	if (new_preview_pending) tryPreview ();
 
 	if (command->getFlags () == START_DEVICE) {
 		int old_devnum = dev_num;
@@ -198,7 +206,7 @@ void RKPreviewBox::updateStatusLabel () {
 		status_label->setText (i18n ("Preview disabled"));
 	} else {
 		if (parentComponent ()->isSatisfied ()) {
-			if (last_plot_done && (!new_plot_pending)) {
+			if (prior_preview_done && (!new_preview_pending)) {
 				status_label->setText (i18n ("Preview up to date"));
 			} else {
 				status_label->setText (i18n ("Preview updating"));
diff --git a/rkward/plugin/rkpreviewbox.h b/rkward/plugin/rkpreviewbox.h
index 4803fbc..11029ea 100644
--- a/rkward/plugin/rkpreviewbox.h
+++ b/rkward/plugin/rkpreviewbox.h
@@ -41,7 +41,7 @@ public:
 	~RKPreviewBox ();
 	int type () { return ComponentPreviewBox; };
 	RKComponentPropertyBool *state;
-	QVariant value (const QString &modifier=QString ()) { return (state->value (modifier)); };
+	QVariant value (const QString &modifier=QString ()) override;
 public slots:
 	void changedState (int);
 	void changedState (RKComponentPropertyBase *);
@@ -53,17 +53,22 @@ protected:
 private:
 	bool updating;		// prevent recursion
 	bool preview_active;
-	bool last_plot_done;
-	bool new_plot_pending;
+	bool prior_preview_done;
+	bool new_preview_pending;
 	void tryPreview ();
 	void killPreview ();
 	void updateStatusLabel ();
 	int dev_num;
+	enum PreviewMode {
+		PlotPreview,
+		DataPreview,
+		CustomPreview
+	} preview_mode;
 	QTimer *update_timer;
 	QCheckBox *toggle_preview_box;
 	QLabel *status_label;
 	RKComponentPropertyCode *code_property;
-	RKComponentPropertyBase *idprop;
+	QString idprop;
 };
 
 #endif
diff --git a/rkward/rbackend/rpackages/rkward/R/internal.R b/rkward/rbackend/rpackages/rkward/R/internal.R
index 0448ad5..82ce60a 100644
--- a/rkward/rbackend/rpackages/rkward/R/internal.R
+++ b/rkward/rbackend/rpackages/rkward/R/internal.R
@@ -447,22 +447,3 @@ assign("available.packages.cache", NULL, envir=.rk.variables)
 	# call separate assignments functions:
 	if (exists (".rk.fix.assignments.graphics")) eval (body (.rk.fix.assignments.graphics)) # internal_graphics.R
 }
-
-assign(".rk.preview.data", list (), envir=.rk.variables)
-
-# TODO document, move somewhere appropriate
-".rk.create.preview.data" <- function (id) {
-	pdata <- .rk.variables$.rk.preview.data
-	if (is.null (pdata[[id]])) {
-		pdata[[id]] <- list ()
-		assign (".rk.preview.data", pdata, envir=.rk.variables)
-	}
-	invisible (pdata[[id]])
-}
-
-".rk.discard.preview.data" <- function (id) {
-	pdata <- .rk.variables$.rk.preview.data
-	pdata[[id]] <- NULL
-	assign (".rk.preview.data", pdata, envir=.rk.variables)
-	invisible (NULL)
-}
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
index 05ef5dc..8cce2c7 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
@@ -33,7 +33,7 @@
 #' @param ask Logical: Whether to ask before flushing the output file.
 #' @return \code{rk.get.tempfile.name}, \code{rk.get.output.html.file}, and
 #'   \code{rk.get.workspace.url} return a string while
-#'   \code{rk.set.output.html.file} returns \code{NULL}.
+#'   \code{rk.set.output.html.file} returns the \bold{previous} output html file.
 #' @author Thomas Friedrichsmeier \email{rkward-devel@@kde.org}
 #' @seealso \url{rkward://page/rkward_output}, \link{tempfile}, \link{file},
 #'   \link{rk.print}
@@ -79,6 +79,7 @@
 #' @rdname rk.get.tempfile.name
 "rk.set.output.html.file" <- function (x, additional.header.contents = getOption ("rk.html.header.additions")) {
 	stopifnot (is.character (x))
+	oldfile <- rk.get.output.html.file ()
 	assign (".rk.output.html.file", x, .rk.variables)
 
 	if (!file.exists (x)) {
@@ -159,7 +160,7 @@
 
 	# needs to come after initialization, so initialization alone does not trigger an update during startup
 	.rk.do.plain.call ("set.output.file", x, synchronous=FALSE)
-	invisible (NULL)
+	invisible (oldfile)
 }
 
 # Internal helper function to extract file names of images used in html files.
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
index 674a1b6..ad70b7d 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
@@ -181,3 +181,75 @@
 	.rk.do.plain.call ("setPluginStatus", c (id, context, visible))
 	invisible (NULL)
 }
+
+assign(".rk.preview.data", list (), envir=.rk.variables)
+
+#' Manage (shortly) persistent data for previews (for use in RKWard plugins wishing to provide custom previews)
+#'
+#' \code{rk.assign.preview.data} stores data associated with a specific "id". Usually this id is
+#'    provided by the <preview>-feature of a plugin.
+#' \code{rk.get.preview.data} retrieves data previously stored with \code{rk.assign.preview.data}
+#' \code{rk.discard.preview.data} discards data previously stored with \code{rk.assign.preview.data}.
+#'    This gets called by the <preview>-box of the plugin, automtically, when the plugin dialog is closed.
+#'    You do not generally have to call it manually. See the notes for running custom clearnup code, below.
+#'
+#' @param id (character). Id associated with the data. Usually this will be the 'id' value of the <preview>-box.
+#' @param value. The value to assign. If this is a list, and contains a function named "on.delete", this function
+#'    will be run by rk.discard.preview.data (with the \code{id} as argument. This is useful for running custom clearnup
+#'    code, such as removing temporary files, etc.
+#'
+#' @return \code{rk.assign.preview.data} amd \code{rk.get.preview.data} returns the preview data (newly) associated
+#'    with the given id. \code{rk.discard.preview.data} returns \code{NULL}, invisibly.
+#'
+#' \bold{Note}: Plugins that want to produce a single plot, or open a single object via \code{\link{rk.edit}()} do \bold{not}
+#'              have to call these functions, manually. See the chapter on providing previews in the Introduction to
+#'              writing plugins for RKWard.
+#'
+#' @author Thomas Friedrichsmeier \email{rkward-devel@@kde.org}
+#' @keywords utilities
+#'
+#' @examples
+#' ## To be generated in the preview() code section of a plugin
+#'
+#' ## NOT RUN
+#' outfile <- rk.get.tempfile.name(prefix="preview", extension=".html")
+#' rk.assign.preview.data("SOMEID", list(filename=outfile, on.delete=function (id) {
+#'   unlink(rk.get.preview.data(id)$filename)
+#' }))
+#' oldfile <- rk.set.output.html.file(f)
+#' try ({
+#'   rk.header("This is a preview of what will happen")
+#'   rk.show.html(rk.get.output.html.file())
+#'   rk.flush.output()
+#' })
+#' rk.set.output.html.file(outfile)
+#' ## END NOT RUN
+#'
+#' @export
+#' @rdname rk.assign.preview.data
+#' @aliases rk.get.preview.data .rk.discard.preview.data
+"rk.assign.preview.data" <- function (id, value=list ()) {
+	pdata <- .rk.variables$.rk.preview.data
+	if (is.null (pdata[[id]])) {
+		pdata[[id]] <- value
+		assign (".rk.preview.data", pdata, envir=.rk.variables)
+		rk.sync (.rk.variables$.rk.preview.data)
+	}
+	invisible (pdata[[id]])
+}
+
+#' @export
+#' @rdname rk.assign.preview.data
+"rk.get.preview.data" <- function (id) {
+	.rk.variables$.rk.preview.data[[id]]
+}
+
+#' @export
+#' @rdname rk.assign.preview.data
+"rk.discard.preview.data" <- function (id) {
+	pdata <- .rk.variables$.rk.preview.data
+	if (!is.null (pdata[[id]]) && !is.null (pdata[[id]]$on.exit)) pdata[[id]]$on.delete (id)
+	pdata[[id]] <- NULL
+	assign (".rk.preview.data", pdata, envir=.rk.variables)
+	invisible (NULL)
+}



More information about the rkward-tracker mailing list