[education/rkward] /: Finish the import assistant dialog.

Thomas Friedrichsmeier null at kde.org
Thu Apr 28 17:35:26 BST 2022


Git commit fdaed27ed31cfbcc143aee56f8564b640e15dc19 by Thomas Friedrichsmeier.
Committed on 28/04/2022 at 16:34.
Pushed by tfry into branch 'master'.

Finish the import assistant dialog.

As part of this, allow "Paste special" to be used stand-alone for pasting objects.

M  +2    -0    ChangeLog
M  +91   -20   rkward/dialogs/rkimportdialog.cpp
M  +11   -4    rkward/dialogs/rkimportdialog.h
M  +33   -5    rkward/misc/rkspecialactions.cpp
M  +7    -2    rkward/misc/rkspecialactions.h
M  +1    -1    rkward/pages/rkward_welcome.rkh
M  +2    -0    rkward/windows/rkhtmlwindow.cpp
M  +13   -3    rkward/windows/robjectbrowser.cpp
M  +2    -1    rkward/windows/robjectbrowser.h

https://invent.kde.org/education/rkward/commit/fdaed27ed31cfbcc143aee56f8564b640e15dc19

diff --git a/ChangeLog b/ChangeLog
index a2cc13b0..1baddbb4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,8 @@
 TODOs:
 	- More tolerant handshake on Windows? Simply a matter of allowing more time?
 
+- (Re-)added improved "Import Assistant" to help getting started with importing data
+- "Paste special" can now also paste data.frames with labels, and is available in the Workspace Browser context menu
 - Fixed: Windows: Issues with some shiny apps due to a too small stack size
 - "Paste special" action gains option to format as data.frame, optionally with labels
 - Add "rio"-based generic import plugin
diff --git a/rkward/dialogs/rkimportdialog.cpp b/rkward/dialogs/rkimportdialog.cpp
index 767af421..ed8fb5e2 100644
--- a/rkward/dialogs/rkimportdialog.cpp
+++ b/rkward/dialogs/rkimportdialog.cpp
@@ -18,12 +18,15 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "../plugin/rkcomponentmap.h"
 #include "../plugin/rkcomponentcontext.h"
 #include "../misc/rkcommonfunctions.h"
+#include "../misc/rkspecialactions.h"
 
 #include "../debug.h"
 
 RKImportDialog::RKImportDialog(const QString &context_id, QWidget *parent) : KAssistantDialog(parent) {
 	RK_TRACE (DIALOGS);
 
+	setWindowTitle(i18n("Import Data Assistant"));
+	rio_handle = RKComponentMap::getComponentHandle("rkward::import_generic_rio");
 	context = RKComponentMap::getContext(context_id);
 	if (context) {
 		component_ids = context->components();
@@ -45,9 +48,12 @@ RKImportDialog::RKImportDialog(const QString &context_id, QWidget *parent) : KAs
 		filters.append (elabel + " [" + filter + "] (" + filter + ')');
 	}
 
-	QWidget *page1 = new QWidget();
-	QVBoxLayout *layout = new QVBoxLayout(page1);
+	QWidget *page = new QWidget();
+	QVBoxLayout *layout = new QVBoxLayout(page);
 	layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("For certain formats, RKWard provides specialized import dialogs, and those generally provide the most options. Is the file you wish to import in one of the following formats?")));
+	if (filters.isEmpty()) {
+		layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("<b>Note:</b> RKWard comes with several import dialogs, but none seem to be loaded, at present. Check your settings.")));
+	}
 	QGroupBox *box = new QGroupBox();
 	layout->addWidget(box);
 	select_format_group = new QButtonGroup(this);
@@ -55,36 +61,101 @@ RKImportDialog::RKImportDialog(const QString &context_id, QWidget *parent) : KAs
 	for (int i = 0; i < filters.size(); ++i) {
 		auto *button = new QRadioButton(filters[i]);
 		sublayout->addWidget(button);
-		select_format_group->addButton(button);
+		select_format_group->addButton(button, i);
 	}
-	auto *button = new QRadioButton("None of the above / try another method");
+	auto *button = new QRadioButton(i18n("None of the above / try another method"));
 	sublayout->addWidget(button);
 	select_format_group->addButton(button);
 	button->setChecked(true);
-	select_format = addPage(page1, i18n("Select format"));
+	connect(select_format_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled), this, &RKImportDialog::updateState);
+	layout->addStretch();
+	select_format = addPage(page, i18n("Select format"));
+
+	page = new QWidget();
+	layout = new QVBoxLayout(page);
+	layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("The 'rio' package offers generic support for importing many different file formats, but requires a number of additional R pacakges to be installed (you will be prompted for missing packages). Do you want to give that a try?")));
+	if (!rio_handle) {
+		layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("<b>Note:</b> The generic import plugin (shipped with RKWard) is not presently loaded. Check your settings.")));
+	}
+	box = new QGroupBox();
+	layout->addWidget(box);
+	select_rio_group = new QButtonGroup(this);
+	sublayout = new QVBoxLayout(box);
+	button = new QRadioButton(i18n("Use 'rio'-based importer"));
+	button->setEnabled(rio_handle != nullptr);
+	sublayout->addWidget(button);
+	select_rio_group->addButton(button, 0);
+	button = new QRadioButton(i18n("Try another method"));
+	sublayout->addWidget(button);
+	select_rio_group->addButton(button);
+	button->setChecked(true);
+	connect(select_rio_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled), this, &RKImportDialog::updateState);
+	layout->addStretch();
+	select_rio = addPage(page, i18n("Generic 'rio'-based importer"));
+
+	page = new QWidget();
+	layout = new QVBoxLayout(page);
+	layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("If your data is moderate in size, and you can open/view it in another application on your system, importing it from clipboard (copy and paste) may be viable.")));
+	box = new QGroupBox();
+	layout->addWidget(box);
+	select_clipboard_group = new QButtonGroup(this);
+	sublayout = new QVBoxLayout(box);
+	button = new QRadioButton(i18n("Import from clipboard"));
+	sublayout->addWidget(button);
+	select_clipboard_group->addButton(button, 0);
+	button = new QRadioButton(i18n("Try another method"));
+	sublayout->addWidget(button);
+	select_clipboard_group->addButton(button);
+	button->setChecked(true);
+	connect(select_clipboard_group, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled), this, &RKImportDialog::updateState);
+	layout->addStretch();
+	select_clipboard = addPage(page, i18n("Import from clipboard"));
+
+	page = new QWidget();
+	layout = new QVBoxLayout(page);
+	layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("<b>Ready to go</b><p>Click \"Finish\" to start the selected import dialog, or \"Back\" to explore other options.</p>")));
+	layout->addStretch();
+	end_with_selection = addPage(page, i18n("Start import dialog"));
+
+	page = new QWidget();
+	layout = new QVBoxLayout(page);
+	layout->addWidget(RKCommonFunctions::wordWrappedLabel(i18n("No further import methods are available at this time. Things you can try, include: <ul><li>Check for the availability of additional import plugins</li><li>Save your data to a different format in the original application</li><li>Ask for advice on rkward-users at kde.org</li></ul>")));
+	layout->addStretch();
+	end_without_selection = addPage(page, i18n("No import method found"));
+
+	updateState();
 }
 
 RKImportDialog::~RKImportDialog() {
 	RK_TRACE(DIALOGS);
 }
 
-/*
+void RKImportDialog::updateState() {
+	RK_TRACE(DIALOGS);
+	bool do_format = (select_format_group->checkedId() >= 0);
+	bool do_rio = !do_format && (select_rio_group->checkedId() == 0);
+	bool do_clipboard = !(do_format || do_rio) && (select_clipboard_group->checkedId() == 0);
+
+	setAppropriate(select_format, true);
+	setAppropriate(select_rio, !do_format);
+	setAppropriate(select_clipboard, !(do_format || do_rio));
+	setAppropriate(end_with_selection, do_format || do_rio || do_clipboard);
+	setAppropriate(end_without_selection, !isAppropriate(end_with_selection));
+}
+
 void RKImportDialog::accept () {
 	RK_TRACE (DIALOGS);
 
-	int index = filters.indexOf (selectedNameFilter ());
-	QString cid = component_ids.value (index);
-	RKComponentHandle *handle = RKComponentMap::getComponentHandle (cid);
-	RKContextHandler *chandler = context->makeContextHandler (this, false);
-
-	if (!(handle && chandler)) {
-		RK_ASSERT (false);
-	} else {
-		RKComponentPropertyBase *filename = new RKComponentPropertyBase (chandler, false);
-		filename->setValue (selectedFiles ().value (0));
-		chandler->addChild ("filename", filename);
-
-		chandler->invokeComponent (handle);
+	hide();
+	int index = select_format_group->checkedId();
+	if (index >= 0) {
+		RKComponentMap::invokeComponent(component_ids.value(index), QStringList());
+	} else if (rio_handle && (select_rio_group->checkedId() == 0)) {
+		rio_handle->invoke(nullptr, nullptr);
+	} else if (select_clipboard_group->checkedId() == 0) {
+		RKPasteSpecialDialog dia(this, true);
+		dia.exec();
 	}
+	deleteLater();
+	KAssistantDialog::accept();
 }
-*/
diff --git a/rkward/dialogs/rkimportdialog.h b/rkward/dialogs/rkimportdialog.h
index 175a25c4..492cb491 100644
--- a/rkward/dialogs/rkimportdialog.h
+++ b/rkward/dialogs/rkimportdialog.h
@@ -14,6 +14,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 class RKComponentGUIXML;
 class KPageWidgetitem;
 class QButtonGroup;
+class RKComponentHandle;
 
 /** This dialog is designed to allow the user to select a file, and file format. After that a suitable plugin
 is opened automatically to deal with this type of file . */
@@ -26,16 +27,22 @@ public:
 	RKImportDialog (const QString &context_id, QWidget *parent);
 /** dtor */
 	~RKImportDialog ();
+	void accept() override;
 private:
+	void updateState();
+
 	QStringList filters;
 	QStringList component_ids;
+	RKComponentHandle *rio_handle;
 	RKComponentGUIXML *context;
 	KPageWidgetItem *select_format;
 	QButtonGroup *select_format_group;
-	KPageWidgetItem *do_format;
-	KPageWidgetItem *select_rio_or_clipboard;
-	KPageWidgetItem *do_rio;
-	KPageWidgetItem *do_clipboard;
+	KPageWidgetItem *select_rio;
+	QButtonGroup *select_rio_group;
+	KPageWidgetItem *select_clipboard;
+	QButtonGroup *select_clipboard_group;
+	KPageWidgetItem *end_with_selection;
+	KPageWidgetItem *end_without_selection;
 };
 
 #endif
diff --git a/rkward/misc/rkspecialactions.cpp b/rkward/misc/rkspecialactions.cpp
index ed8b3655..9b82c448 100644
--- a/rkward/misc/rkspecialactions.cpp
+++ b/rkward/misc/rkspecialactions.cpp
@@ -8,6 +8,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "rkspecialactions.h"
 
 #include <KLocalizedString>
+#include <KMessageBox>
+
+#include "../rkward.h"
 
 #include "../debug.h"
 
@@ -50,13 +53,21 @@ void RKPasteSpecialAction::doSpecialPaste() {
 
 #include "../dataeditor/rktextmatrix.h"
 #include "../core/robject.h"
+#include "rksaveobjectchooser.h"
+#include "../rbackend/rkrinterface.h"
+#include "../misc/rkprogresscontrol.h"
 
-RKPasteSpecialDialog::RKPasteSpecialDialog (QWidget* parent) : QDialog (parent) {
+RKPasteSpecialDialog::RKPasteSpecialDialog(QWidget* parent, bool standalone) : QDialog(parent) {
 	RK_TRACE (MISC);
 
 	setWindowTitle (i18n ("Paste Special..."));
 
 	QVBoxLayout *pagelayout = new QVBoxLayout (this);
+	objectname = standalone ? new RKSaveObjectChooser(this, QStringLiteral("pasted.data")) : nullptr;
+	if (objectname) {
+		connect(objectname, &RKSaveObjectChooser::changed, this, &RKPasteSpecialDialog::updateState);
+		pagelayout->addWidget(objectname);
+	}
 	QHBoxLayout *rowlayout = new QHBoxLayout ();
 	pagelayout->addLayout (rowlayout);
 
@@ -138,9 +149,9 @@ RKPasteSpecialDialog::RKPasteSpecialDialog (QWidget* parent) : QDialog (parent)
 	// Labels
 	box = new QGroupBox(i18n("Labels"), this);
 	group_layout = new QVBoxLayout (box);
-	names_box = new QCheckBox(i18n("First column contains labels"), box);
+	names_box = new QCheckBox(i18n("First row contains labels"), box);
 	group_layout->addWidget(names_box);
-	rownames_box = new QCheckBox(i18n("First row contains labels"), box);
+	rownames_box = new QCheckBox(i18n("First column contains labels"), box);
 	group_layout->addWidget(rownames_box);
 	rowlayout->addWidget(box);
 
@@ -159,8 +170,9 @@ RKPasteSpecialDialog::RKPasteSpecialDialog (QWidget* parent) : QDialog (parent)
 	rowlayout->addWidget (box);
 
 	QDialogButtonBox *buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
-	connect (buttons->button (QDialogButtonBox::Ok), &QPushButton::clicked, this, &QDialog::accept);
-	connect (buttons->button (QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QDialog::reject);
+	ok_button = buttons->button(QDialogButtonBox::Ok);
+	connect(ok_button, &QPushButton::clicked, this, &QDialog::accept);
+	connect(buttons->button (QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QDialog::reject);
 	pagelayout->addWidget (buttons);
 
 	updateState ();		// initialize
@@ -184,6 +196,7 @@ void RKPasteSpecialDialog::updateState () {
 	rownames_box->setEnabled(dimensionality == DimDataFrame);
 
 	separator_freefield->setEnabled ((dimensionality != DimSingleString) && (separator_group->checkedId () == SepCustom));
+	ok_button->setEnabled((objectname == nullptr) || objectname->isOk());
 }
 
 QString RKPasteSpecialDialog::resultingText () {
@@ -284,3 +297,18 @@ QString RKPasteSpecialDialog::prepString(const QString& src, const Quoting quot)
 	return src;
 }
 
+void RKPasteSpecialDialog::accept() {
+	RK_TRACE(MISC);
+	if (objectname) {
+		RCommand *command = new RCommand(objectname->currentFullName() + " <- " + resultingText(), RCommand::App | RCommand::ObjectListUpdate);
+		connect(command->notifier(), &RCommandNotifier::commandFinished, [](RCommand *c) {
+			if (c->failed()) {
+				QString msg = c->fullOutput();
+				if (msg.isEmpty()) msg = i18n("Command failed to parse. Try using <i>Edit->Paste special...</i> in the R Console window for better diagnostics.");
+				KMessageBox::detailedError(RKWardMainWindow::getMain(), i18n("Pasting object from clipboard data failed."), msg, i18n("Paste failed"));
+			}
+		});
+		RInterface::issueCommand(command);
+	}
+	QDialog::accept();
+}
diff --git a/rkward/misc/rkspecialactions.h b/rkward/misc/rkspecialactions.h
index fd4e59ee..c1d317e4 100644
--- a/rkward/misc/rkspecialactions.h
+++ b/rkward/misc/rkspecialactions.h
@@ -32,12 +32,14 @@ signals:
 class QButtonGroup;
 class QLineEdit;
 class QCheckBox;
+class RKSaveObjectChooser;
 
-/** Dialog used in RKPasteSpecialAction */
+/** Dialog used in RKPasteSpecialAction
+    TODO: move to separate file, now that it can be used standalone */
 class RKPasteSpecialDialog : public QDialog {
 	Q_OBJECT
 public:
-	explicit RKPasteSpecialDialog (QWidget* parent);
+	explicit RKPasteSpecialDialog(QWidget* parent, bool standalone=false);
 	~RKPasteSpecialDialog ();
 
 	enum Dimensionality {
@@ -60,6 +62,7 @@ public:
 	};
 	
 	QString resultingText ();
+	void accept() override;
 public slots:
 	void updateState ();
 private:
@@ -75,6 +78,8 @@ private:
 	QCheckBox* insert_nas_box;
 	QCheckBox* names_box;
 	QCheckBox* rownames_box;
+	RKSaveObjectChooser *objectname;
+	QPushButton *ok_button;
 };
 
 #endif
diff --git a/rkward/pages/rkward_welcome.rkh b/rkward/pages/rkward_welcome.rkh
index 2a1944a3..e536a0dd 100644
--- a/rkward/pages/rkward_welcome.rkh
+++ b/rkward/pages/rkward_welcome.rkh
@@ -11,7 +11,7 @@
 	<section title="Import or enter data" id="import-create">
 		<ul>
 	<li><link href="rkward://actions/new_data_frame">Enter new data</link></li>
-	<li>Import data</li>
+	<li><link href="rkward://actions/import_assistant">Import data</link></li>
 		</ul>
 	</section>
 
diff --git a/rkward/windows/rkhtmlwindow.cpp b/rkward/windows/rkhtmlwindow.cpp
index f81920dd..37b00da4 100644
--- a/rkward/windows/rkhtmlwindow.cpp
+++ b/rkward/windows/rkhtmlwindow.cpp
@@ -605,6 +605,8 @@ bool RKHTMLWindow::handleRKWardURL (const QUrl &url, RKHTMLWindow *window) {
 				RKWardMainWindow::getMain()->slotNewDataFrame();
 			} else if (action == "rpackage_install") {
 				RKWardMainWindow::getMain()->slotFileLoadLibs();
+			} else if (action == "import_assistant") {
+				RKWardMainWindow::getMain()->importData();
 			} else {
 				RK_ASSERT(false);
 			}
diff --git a/rkward/windows/robjectbrowser.cpp b/rkward/windows/robjectbrowser.cpp
index 79e3a3e2..94bdff34 100644
--- a/rkward/windows/robjectbrowser.cpp
+++ b/rkward/windows/robjectbrowser.cpp
@@ -1,17 +1,19 @@
 /*
 robjectbrowser - This file is part of RKWard (https://rkward.kde.org). Created: Thu Aug 19 2004
-SPDX-FileCopyrightText: 2004-2017 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileCopyrightText: 2004-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
 SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
 SPDX-License-Identifier: GPL-2.0-or-later
 */
 #include "robjectbrowser.h"
 
-#include <qlayout.h>
-#include <qpushbutton.h>
+#include <QPushButton>
 #include <QFocusEvent>
 #include <QVBoxLayout>
 #include <QMenu>
 #include <QInputDialog>
+#include <QApplication>
+#include <QMimeData>
+#include <QClipboard>
 
 #include <KLocalizedString>
 #include <kmessagebox.h>
@@ -26,6 +28,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
 #include "../misc/rkdummypart.h"
 #include "../misc/rkstandardicons.h"
 #include "../misc/rkstandardactions.h"
+#include "../misc/rkspecialactions.h"
 #include "rkworkplace.h"
 #include "../dataeditor/rkeditor.h"
 
@@ -121,6 +124,8 @@ RObjectBrowserInternal::RObjectBrowserInternal (QWidget *parent, RObjectBrowser
 	connect (actions[CopyToGlobalEnv], &QAction::triggered, this, &RObjectBrowserInternal::popupCopyToGlobalEnv);
 	actions.insert (Delete, new QAction (i18n ("Delete"), this));
 	connect (actions[Delete], &QAction::triggered, this, &RObjectBrowserInternal::popupDelete);
+	actions.insert(NewFromClipboard, new QAction(QIcon::fromTheme("edit-paste"), i18n("New object from clipboard"), this));
+	connect (actions[NewFromClipboard], &QAction::triggered, this, []() { RKPasteSpecialDialog dia(RKWardMainWindow::getMain(), true); dia.exec(); });
 	actions.insert (Unload, new QAction (i18n ("Unload Package"), this));
 	connect (actions[Unload], &QAction::triggered, this, &RObjectBrowserInternal::popupUnload);
 	actions.insert (LoadUnloadPackages, new QAction (i18n ("Load / Unload Packages"), this));
@@ -253,6 +258,11 @@ void RObjectBrowserInternal::contextMenuCallback (RObject *, bool *) {
 	actions[Copy]->setVisible (object->canRead () && (!object->isType (RObject::ToplevelEnv)));
 	actions[CopyToGlobalEnv]->setVisible (object->canRead () && (!object->isInGlobalEnv()) && (!object->isType (RObject::ToplevelEnv)));
 	actions[Delete]->setVisible (object->canRemove ());
+	{
+		const QClipboard *clipboard = QApplication::clipboard();
+		const QMimeData *mime_data = clipboard->mimeData();
+		actions[NewFromClipboard]->setEnabled(mime_data->hasText());
+	}
 	actions[Unload]->setVisible (object->isType (RObject::PackageEnv));
 	actions[LoadUnloadPackages]->setVisible (object == RObjectList::getObjectList ());
 }
diff --git a/rkward/windows/robjectbrowser.h b/rkward/windows/robjectbrowser.h
index d01dc358..6e26d592 100644
--- a/rkward/windows/robjectbrowser.h
+++ b/rkward/windows/robjectbrowser.h
@@ -1,6 +1,6 @@
 /*
 robjectbrowser - This file is part of the RKWard project. Created: Thu Aug 19 2004
-SPDX-FileCopyrightText: 2004-2016 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileCopyrightText: 2004-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
 SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
 SPDX-License-Identifier: GPL-2.0-or-later
 */
@@ -83,6 +83,7 @@ private:
 		Copy,
 		CopyToGlobalEnv,
 		Delete,
+		NewFromClipboard,
 		Unload,
 		LoadUnloadPackages,
 		ActionCount


More information about the rkward-tracker mailing list