Skip to content

Commit ce320fd

Browse files
committed
Array editor: add "Paste" feature to paste data from clipboard
Fix #87
1 parent ef4549f commit ce320fd

File tree

3 files changed

+61
-4
lines changed

3 files changed

+61
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
💥 New features:
66

7+
* [Issue #87](https://github.com/PlotPyStack/guidata/issues/87) - Array editor: add an option to paste data (Ctrl+V)
78
* [Issue #85](https://github.com/PlotPyStack/guidata/issues/85) - Array editor: add a button to export data as CSV
89
* [Issue #86](https://github.com/PlotPyStack/guidata/issues/86) - Array editor: add "Copy all" feature for copying array and headers to clipboard
910

guidata/locale/fr/LC_MESSAGES/guidata.po

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
msgid ""
77
msgstr ""
88
"Project-Id-Version: PACKAGE VERSION\n"
9-
"POT-Creation-Date: 2025-04-26 10:25+0200\n"
9+
"POT-Creation-Date: 2025-04-26 11:12+0200\n"
1010
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1111
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1212
"Language-Team: LANGUAGE <[email protected]>\n"
@@ -270,6 +270,9 @@ msgstr "Suppression de colonne(s)"
270270
msgid "Copy"
271271
msgstr "Copier"
272272

273+
msgid "Paste"
274+
msgstr "Coller"
275+
273276
msgid "About..."
274277
msgstr "A propos..."
275278

@@ -291,6 +294,9 @@ msgstr "Avertissement"
291294
msgid "It was not possible to copy values for this array"
292295
msgstr "Impossible de copier les valeurs pour ce tableau"
293296

297+
msgid "It was not possible to paste values for this array"
298+
msgstr "Impossible de coller les valeurs pour ce tableau"
299+
294300
msgid "Format"
295301
msgstr "Format"
296302

@@ -379,9 +385,6 @@ msgstr ""
379385
msgid "Resize rows to contents"
380386
msgstr "Ajuster les colonnes au contenu"
381387

382-
msgid "Paste"
383-
msgstr "Coller"
384-
385388
msgid "Edit"
386389
msgstr "Modifier"
387390

guidata/widgets/arrayeditor/editorwidget.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ def __init__(
185185
total_width += self.columnWidth(k)
186186
self.viewport().resize(min(total_width, 1024), self.height())
187187
QShortcut(QKeySequence(QKeySequence.Copy), self, self.copy)
188+
QShortcut(QKeySequence(QKeySequence.Paste), self, self.paste)
188189
self.horizontalScrollBar().valueChanged.connect(
189190
lambda val: self.load_more_data(val, columns=True)
190191
)
@@ -409,6 +410,15 @@ def setup_cell_menu(self) -> QMenu:
409410
triggered=self.copy,
410411
context=Qt.ShortcutContext.WidgetShortcut,
411412
)
413+
self.paste_action = create_action(
414+
self,
415+
_("Paste"),
416+
shortcut=keybinding("Paste"),
417+
icon=get_icon("editpaste.png"),
418+
triggered=self.paste,
419+
context=Qt.ShortcutContext.WidgetShortcut,
420+
)
421+
self.paste_action.setDisabled(self.model().readonly)
412422
about_action = create_action(
413423
self,
414424
_("About..."),
@@ -442,6 +452,7 @@ def setup_cell_menu(self) -> QMenu:
442452
)
443453
actions = (
444454
self.copy_action,
455+
self.paste_action,
445456
None,
446457
insert_row_action,
447458
insert_col_action,
@@ -454,6 +465,7 @@ def setup_cell_menu(self) -> QMenu:
454465
else:
455466
actions = (
456467
self.copy_action,
468+
self.paste_action,
457469
None,
458470
about_action,
459471
)
@@ -587,6 +599,47 @@ def copy(self) -> None:
587599
clipboard = QApplication.clipboard()
588600
clipboard.setText(cliptxt)
589601

602+
@Slot()
603+
def paste(self) -> None:
604+
"""Paste text from clipboard"""
605+
cliptxt = QApplication.clipboard().text()
606+
if not cliptxt:
607+
return
608+
try:
609+
data = np.genfromtxt(io.StringIO(cliptxt), delimiter="\t")
610+
except ValueError:
611+
data = np.array([])
612+
if data.size == 0:
613+
QMessageBox.warning(
614+
self,
615+
_("Warning"),
616+
_("It was not possible to paste values for this array"),
617+
)
618+
return
619+
620+
model = self.model()
621+
622+
# Determine where to paste
623+
start_row = self.currentIndex().row()
624+
start_col = self.currentIndex().column()
625+
626+
# Iterate and paste each value
627+
data = np.array(data, dtype=model.get_array().dtype)
628+
if data.ndim in (0, 1):
629+
data = data.reshape((-1, 1))
630+
for i in range(data.shape[0]):
631+
for j in range(data.shape[1]):
632+
if (start_row + i < model.total_rows) and (
633+
start_col + j < model.total_cols
634+
):
635+
idx = model.index(start_row + i, start_col + j)
636+
model.setData(idx, str(data[i, j]))
637+
638+
model.dataChanged.emit(
639+
model.index(start_row, start_col),
640+
model.index(start_row + data.shape[0] - 1, start_col + data.shape[1] - 1),
641+
)
642+
590643

591644
class BaseArrayEditorWidget(QWidget):
592645
"""Base ArrayEditdorWidget class. Used to wrap handle n-dimensional normal Numpy's

0 commit comments

Comments
 (0)