[rkward] rkward: Add more preview modes.

Thomas Friedrichsmeier null at kde.org
Fri Sep 28 21:28:40 BST 2018


Git commit f4a6d5af63a9116da5482238078b14ce348d2f7b by Thomas Friedrichsmeier.
Committed on 28/09/2018 at 20:20.
Pushed by tfry into branch 'master'.

Add more preview modes.

Known quirks, so far:
- Caption is always "Preview of rendered R markdown"
- R Console preview shows commands and output lines in an off-by-one fashion (first two commands, then first result)
- Switching between the modes is still quirky, can probably be solved by closing the previous preview, explicitly.

M  +1    -1    rkward/rbackend/rkrbackend.cpp
M  +3    -2    rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
M  +79   -31   rkward/windows/rkcommandeditorwindow.cpp
M  +12   -2    rkward/windows/rkcommandeditorwindow.h

https://commits.kde.org/rkward/f4a6d5af63a9116da5482238078b14ce348d2f7b

diff --git a/rkward/rbackend/rkrbackend.cpp b/rkward/rbackend/rkrbackend.cpp
index 6754d0ad..b35be9ef 100644
--- a/rkward/rbackend/rkrbackend.cpp
+++ b/rkward/rbackend/rkrbackend.cpp
@@ -1603,7 +1603,7 @@ QStringList RKRBackend::handlePlainGenericRequest (const QStringList &parameters
 		output_file = parameters.value (1);
 		if (parameters.length () > 2) {
 			RK_ASSERT (parameters.value (2) == "SILENT");
-			return QStringList ();		// For automated testing. The frontend should not be notified, here
+			return QStringList ();		// For automated testing and previews. The frontend should not be notified, here
 		}
 		request.params["call"] = parameters;
 	} else {
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
index 472dd8e0..f8072c8a 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
@@ -33,6 +33,7 @@
 #' @param css Local file name of CSS file to use, or NULL for no CSS file. The CSS file will be
 #'            placed next to x, with file name extension ".css". Only effective when initializing a
 #'            (non-existing) output file.
+#' @param silent Set to true to avoid the output window being raised in the frontend.
 #' @param flush.images. If true, any images used in the output file will be deleted as well.
 #' @param ask Logical: Whether to ask before flushing the output file.
 #' @param ... Further parameters passed to rk.set.output.html.file()
@@ -82,7 +83,7 @@
 
 #' @export
 #' @rdname rk.get.tempfile.name
-"rk.set.output.html.file" <- function (x, additional.header.contents = getOption ("rk.html.header.additions"), style=c ("regular", "preview"), css = getOption ("rk.output.css.file")) {
+"rk.set.output.html.file" <- function (x, additional.header.contents = getOption ("rk.html.header.additions"), style=c ("regular", "preview"), css = getOption ("rk.output.css.file"), silent=FALSE) {
 	stopifnot (is.character (x))
 	style <- match.arg (style)
 	oldfile <- rk.get.output.html.file ()
@@ -177,7 +178,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)
+	.rk.do.plain.call ("set.output.file", c (x, if (isTRUE (silent)) "SILENT" else NULL), synchronous=FALSE)
 	invisible (oldfile)
 }
 
diff --git a/rkward/windows/rkcommandeditorwindow.cpp b/rkward/windows/rkcommandeditorwindow.cpp
index 4ca22e4b..776c7f38 100644
--- a/rkward/windows/rkcommandeditorwindow.cpp
+++ b/rkward/windows/rkcommandeditorwindow.cpp
@@ -35,6 +35,7 @@
 #include <QMenu>
 #include <QAction>
 #include <QTemporaryFile>
+#include <QTemporaryDir>
 #include <QDir>
 #include <QSplitter>
 
@@ -47,6 +48,7 @@
 #include <kio/job.h>
 #include <kconfiggroup.h>
 #include <krandom.h>
+#include <KSelectAction>
 
 #include "../misc/rkcommonfunctions.h"
 #include "../misc/rkstandardicons.h"
@@ -94,6 +96,7 @@ RKCommandEditorWindow::RKCommandEditorWindow (QWidget *parent, const QUrl _url,
 
 	QUrl url = _url;
 	m_doc = 0;
+	preview_dir = 0;
 
 	// Lookup of existing text editor documents: First, if no url is given at all, create a new document, and register an id, in case this window will get split, later
 	if (url.isEmpty ()) {
@@ -274,6 +277,7 @@ RKCommandEditorWindow::~RKCommandEditorWindow () {
 	if (have_url && !_id.isEmpty ()) {
 		unnamed_documents.remove (_id);
 	}
+	delete preview_dir;
 }
 
 void RKCommandEditorWindow::fixupPartGUI () {
@@ -331,10 +335,17 @@ void RKCommandEditorWindow::initializeActions (KActionCollection* ac) {
 	action_setwd_to_script->setToolTip (action_setwd_to_script->statusTip ());
 	action_setwd_to_script->setIcon (RKStandardIcons::getIcon (RKStandardIcons::ActionCDToScript));
 
-	action_render_preview = ac->addAction ("render_preview", this, SLOT (textChanged()));
-	action_render_preview->setText ("Render Preview");
-	action_render_preview->setCheckable (true);
-	connect (preview, &RKXMLGUIPreviewArea::previewClosed, action_render_preview, &QAction::toggle);
+	actionmenu_preview = new KSelectAction (QIcon::fromTheme ("view-preview"), i18n ("Preview"), this);
+	actionmenu_preview->addAction (RKStandardIcons::getIcon (RKStandardIcons::ActionDelete), i18n ("No preview"));
+	actionmenu_preview->addAction (QIcon::fromTheme("preview_math"), i18n ("R Markdown"));
+	actionmenu_preview->addAction (RKStandardIcons::getIcon (RKStandardIcons::WindowOutput), i18n ("RKWard Output"));
+	actionmenu_preview->addAction (RKStandardIcons::getIcon (RKStandardIcons::WindowX11), i18n ("Plot"));
+	actionmenu_preview->addAction (RKStandardIcons::getIcon (RKStandardIcons::WindowConsole), i18n ("R Console"));
+	actionmenu_preview->setCurrentItem (NoPreview);
+	actionmenu_preview->setToolBarMode (KSelectAction::MenuMode);
+	connect (preview, &RKXMLGUIPreviewArea::previewClosed, [this]() { actionmenu_preview->setCurrentItem (NoPreview); });
+	connect (actionmenu_preview, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &RKCommandEditorWindow::textChanged);
+	ac->addAction ("render_preview", actionmenu_preview);
 	
 	file_save = findAction (m_view, "file_save");
 	if (file_save) file_save->setText (i18n ("Save Script..."));
@@ -491,7 +502,7 @@ void RKCommandEditorWindow::textChanged () {
 	RK_TRACE (COMMANDEDITOR);
 
 	// render preview
-	if (action_render_preview->isChecked ()) {
+	if (actionmenu_preview->currentItem () != NoPreview) {
 		preview_manager->setUpdatePending ();
 		preview_timer.start (500);              // brief delay to buffer keystrokes
 	} else {
@@ -793,35 +804,72 @@ void RKCommandEditorWindow::copyLinesToOutput () {
 void RKCommandEditorWindow::doRenderPreview () {
 	RK_TRACE (COMMANDEDITOR);
 
-	if (action_render_preview->isChecked ()) {
-		if (!preview_manager->needsCommand ()) return;
-
-		QTemporaryFile save (QDir::tempPath () + QStringLiteral ("/rkward_XXXXXX") + preview_manager->previewId () + QStringLiteral (".Rmd"));
-		RK_ASSERT (save.open ());
-		QTextStream out (&save);
-		out.setCodec ("UTF-8");     // make sure that all characters can be saved, without nagging the user
-		out << m_doc->text ();
-		save.close ();
-		save.setAutoRemove (false);
-
-		QString command ("if (!nzchar(Sys.which(\"pandoc\"))) {\n"
-		                 "	output <- rk.set.output.html.file(%2)\n"
-		                 "	rk.header (" + RObject::rQuote (i18n ("Pandoc is not installed")) + ")\n"
-		                 "	rk.print (" + RObject::rQuote (i18n ("The software <tt>pandoc</tt>, required ot rendering R markdown files, is not installed, or not in the system path of "
-		                           "the running R session. You will need to install pandoc from <a href=\"https://pandoc.org/\">https://pandoc.org/</a>.</br>"
-		                           "If is installed, but cannot be found, try adding it to the system path of the running R session at "
-		                           "<a href=\"rkward://settings/rbackend\">Settings->Configure RKward->R-backend</a>.")) + ")\n"
-		                 "	rk.set.output.html.file(output)\n"
-		                 "} else {\n"
-		                 "	require(rmarkdown)\n"
-		                 "	rmarkdown::render (%1, output_format=\"html_document\", output_file=%2, quiet=TRUE)\n"
-		                 "}\n"
-		                 "rk.show.html(%2)\n");
+	if (actionmenu_preview->currentItem () == NoPreview) return;
+	if (!preview_manager->needsCommand ()) return;
+
+	if (!preview_dir) preview_dir = new QTemporaryDir ();
+	QFile save (preview_dir->filePath ("script.R"));
+	RK_ASSERT (save.open (QIODevice::WriteOnly));
+	QTextStream out (&save);
+	out.setCodec ("UTF-8");     // make sure that all characters can be saved, without nagging the user
+	out << m_doc->text ();
+	save.close ();
+
+	QString command;
+	if (actionmenu_preview->currentItem () == RMarkdownPreview) {
+		save.rename (preview_dir->filePath ("markdownscript.Rmd"));
+
+		command = "if (!nzchar(Sys.which(\"pandoc\"))) {\n"
+		           "	output <- rk.set.output.html.file(%2, silent=TRUE)\n"
+		           "	rk.header (" + RObject::rQuote (i18n ("Pandoc is not installed")) + ")\n"
+		           "	rk.print (" + RObject::rQuote (i18n ("The software <tt>pandoc</tt>, required ot rendering R markdown files, is not installed, or not in the system path of "
+		                         "the running R session. You will need to install pandoc from <a href=\"https://pandoc.org/\">https://pandoc.org/</a>.</br>"
+		                         "If is installed, but cannot be found, try adding it to the system path of the running R session at "
+		                         "<a href=\"rkward://settings/rbackend\">Settings->Configure RKward->R-backend</a>.")) + ")\n"
+		           "	rk.set.output.html.file(output, silent=TRUE)\n"
+		           "} else {\n"
+		           "	require(rmarkdown)\n"
+		           "	rmarkdown::render (%1, output_format=\"html_document\", output_file=%2, quiet=TRUE)\n"
+		           "}\n"
+		           "rk.show.html(%2)\n";
 		command = command.arg (RObject::rQuote (save.fileName ()), RObject::rQuote (save.fileName () + ".html"));
+	} else if (actionmenu_preview->currentItem () == RKOutputPreview) {
+		QString output_file = preview_dir->filePath ("output.html");
+		command = "output <- rk.set.output.html.file(%2, silent=TRUE)\n"
+		          "try(rk.flush.output(ask=FALSE, style=\"preview\", silent=TRUE))\n"
+		          "try(source(%1))\n"
+		          "rk.set.output.html.file(output, silent=TRUE)\n"
+		          "rk.show.html(%2)\n";
+		command = command.arg (RObject::rQuote (save.fileName ()), RObject::rQuote (output_file));
+	} else if (actionmenu_preview->currentItem () == GraphPreview) {
+		command = "olddev <- dev.cur()\n"
+		          "RK()\n"
+		          "try(source(%1))\n"
+		          "dev.set(olddev)\n";
+		command = command.arg (RObject::rQuote (save.fileName ()));
+	} else {
+		RK_ASSERT (actionmenu_preview->currentItem () == ConsolePreview);
+	}
+
+	preview->wrapperWidget ()->show ();
+
+	if (actionmenu_preview->currentItem () == ConsolePreview) {  // somewhat hacky, I admit...
+		QString output_file = RObject::rQuote (preview_dir->filePath ("output.html"));
+		RKGlobals::rInterface ()->issueCommand (QString (
+		                                        ".rk.with.window.hints ({\n"
+		                                        "   rk.assign.preview.data(%1, rk.set.output.html.file(%2, silent=TRUE))\n"
+		                                        "   rk.flush.output(ask=FALSE, style=\"preview\", silent=TRUE)\n"
+		                                        "}, \"\", %1, style=\"preview\")\n").arg (RObject::rQuote (preview_manager->previewId ()), output_file), RCommand::App);
 
-		RCommand *rcommand = new RCommand (".rk.with.window.hints (local ({\n" + command + QStringLiteral ("}), \"\", ") + RObject::rQuote (preview_manager->previewId ()) + ')', RCommand::App);
+		RCommand *rcommand = new RCommand (m_doc->text(), RCommand::User | RCommand::CCCommand | RCommand::CCOutput);
+		preview_manager->setCommand (rcommand);
+		RKGlobals::rInterface ()->issueCommand (QString ("rk.set.output.html.file(rk.get.preview.data(%1), silent=TRUE)\n"
+		                                        ".rk.with.window.hints ({\n"
+		                                        "	rk.show.html(%2)\n"
+		                                        "}, \"\", %1, style=\"preview\")\n").arg (RObject::rQuote (preview_manager->previewId ()), output_file), RCommand::App);
+	} else {
+		RCommand *rcommand = new RCommand (".rk.with.window.hints (local ({\n" + command + QStringLiteral ("}), \"\", ") + RObject::rQuote (preview_manager->previewId ()) + ", style=\"preview\")", RCommand::App);
 		preview_manager->setCommand (rcommand);
-		preview->wrapperWidget ()->show ();
 	}
 }
 
diff --git a/rkward/windows/rkcommandeditorwindow.h b/rkward/windows/rkcommandeditorwindow.h
index e1bf9a2b..cf1b3f59 100644
--- a/rkward/windows/rkcommandeditorwindow.h
+++ b/rkward/windows/rkcommandeditorwindow.h
@@ -39,8 +39,9 @@ class QCloseEvent;
 class QFrame;
 class QLabel;
 class QAction;
-class QAction;
+class QTemporaryDir;
 class KActionMenu;
+class KSelectAction;
 class RKCommandEditorWindow;
 class KActionCollection;
 
@@ -261,7 +262,15 @@ private:
 
 	QAction* action_run_all;
 	QAction* action_run_current;
-	QAction* action_render_preview;
+	KSelectAction* actionmenu_preview;
+
+	enum PreviewMode {
+		NoPreview,
+		RMarkdownPreview,
+		RKOutputPreview,
+		GraphPreview,
+		ConsolePreview,
+	};
 
 	QAction* action_setwd_to_script;
 
@@ -276,6 +285,7 @@ private:
 	RKXMLGUIPreviewArea *preview;
 	QTimer preview_timer;
 	RKPreviewManager *preview_manager;
+	QTemporaryDir *preview_dir;
 };
 
 /** Simple class to provide HTML highlighting for arbitrary R code. */



More information about the rkward-tracker mailing list