Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Test and Score: Copy selected rows to clipboard #5203

Merged
merged 3 commits into from
Jan 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 3 additions & 45 deletions Orange/widgets/data/owtable.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import sys
import threading
import io
import csv
import itertools
import concurrent.futures

Expand All @@ -18,7 +16,7 @@
)
from AnyQt.QtGui import QColor, QClipboard
from AnyQt.QtCore import (
Qt, QSize, QEvent, QByteArray, QMimeData, QObject, QMetaObject,
Qt, QSize, QEvent, QObject, QMetaObject,
QAbstractProxyModel, QIdentityProxyModel, QModelIndex,
QItemSelectionModel, QItemSelection, QItemSelectionRange,
)
Expand All @@ -35,7 +33,8 @@
from Orange.widgets.utils.itemselectionmodel import (
BlockSelectionModel, ranges, selection_blocks
)
from Orange.widgets.utils.tableview import TableView
from Orange.widgets.utils.tableview import TableView, \
table_selection_to_mime_data
from Orange.widgets.utils.widgetpreview import WidgetPreview
from Orange.widgets.widget import OWWidget, Input, Output
from Orange.widgets.utils import datacaching
Expand Down Expand Up @@ -163,47 +162,6 @@ def rowCount(self, parent=QModelIndex()):
return stop - start


def table_selection_to_mime_data(table):
"""Copy the current selection in a QTableView to the clipboard.
"""
lines = table_selection_to_list(table)

as_csv = lines_to_csv_string(lines, dialect="excel").encode("utf-8")
as_tsv = lines_to_csv_string(lines, dialect="excel-tab").encode("utf-8")

mime = QMimeData()
mime.setData("text/csv", QByteArray(as_csv))
mime.setData("text/tab-separated-values", QByteArray(as_tsv))
mime.setData("text/plain", QByteArray(as_tsv))
return mime


def lines_to_csv_string(lines, dialect="excel"):
stream = io.StringIO()
writer = csv.writer(stream, dialect=dialect)
writer.writerows(lines)
return stream.getvalue()


def table_selection_to_list(table):
model = table.model()
indexes = table.selectedIndexes()

rows = sorted(set(index.row() for index in indexes))
columns = sorted(set(index.column() for index in indexes))

lines = []
for row in rows:
line = []
for col in columns:
val = model.index(row, col).data(Qt.DisplayRole)
# TODO: use style item delegate displayText?
line.append(str(val))
lines.append(line)

return lines


TableSlot = namedtuple("TableSlot", ["input_id", "table", "summary", "view"])


Expand Down
3 changes: 3 additions & 0 deletions Orange/widgets/evaluate/owtestandscore.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,9 @@ def onDeleteWidget(self):
self.__executor.shutdown(wait=False)
super().onDeleteWidget()

def copy_to_clipboard(self):
self.score_table.copy_selection_to_clipboard()


class UserInterrupt(BaseException):
"""
Expand Down
17 changes: 17 additions & 0 deletions Orange/widgets/evaluate/tests/test_owtestandscore.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import numpy as np
from AnyQt.QtCore import Qt
from AnyQt.QtTest import QTest
from AnyQt.QtWidgets import QApplication
import baycomp

from Orange.classification import MajorityLearner, LogisticRegressionLearner, \
Expand Down Expand Up @@ -693,6 +694,22 @@ def test_unique_output_domain(self):
output = self.get_output(self.widget.Outputs.predictions)
self.assertEqual(output.domain.metas[0].name, 'random forest (1)')

def test_copy_to_clipboard(self):
self.send_signal(self.widget.Inputs.train_data, Table("iris"))
self.send_signal(self.widget.Inputs.learner, RandomForestLearner(), 0)
self.wait_until_finished()
view = self.widget.score_table.view
model = self.widget.score_table.model
selection_model = view.selectionModel()
selection_model.select(model.index(0, 0),
selection_model.Select | selection_model.Rows)

self.widget.copy_to_clipboard()
clipboard_text = QApplication.clipboard().text()
view_text = "\t".join([str(model.data(model.index(0, i)))
for i in (0, 3, 4, 5, 6, 7)]) + "\r\n"
self.assertEqual(clipboard_text, view_text)


class TestHelpers(unittest.TestCase):
def test_results_one_vs_rest(self):
Expand Down
12 changes: 10 additions & 2 deletions Orange/widgets/evaluate/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@

import numpy as np

from AnyQt.QtWidgets import QHeaderView, QStyledItemDelegate, QMenu
from AnyQt.QtGui import QStandardItemModel, QStandardItem
from AnyQt.QtWidgets import QHeaderView, QStyledItemDelegate, QMenu, \
QApplication
from AnyQt.QtGui import QStandardItemModel, QStandardItem, QClipboard
from AnyQt.QtCore import Qt, QSize, QObject, pyqtSignal as Signal, \
QSortFilterProxyModel
from sklearn.exceptions import UndefinedMetricWarning

from Orange.data import Variable, DiscreteVariable, ContinuousVariable
from Orange.evaluation import scoring
from Orange.widgets import gui
from Orange.widgets.utils.tableview import table_selection_to_mime_data
from Orange.widgets.gui import OWComponent
from Orange.widgets.settings import Setting

Expand Down Expand Up @@ -209,3 +211,9 @@ def update_header(self, scorers):
item.setToolTip(score.long_name)
self.model.setHorizontalHeaderItem(col, item)
self._update_shown_columns()

def copy_selection_to_clipboard(self):
mime = table_selection_to_mime_data(self.view)
QApplication.clipboard().setMimeData(
mime, QClipboard.Clipboard
)
47 changes: 46 additions & 1 deletion Orange/widgets/utils/tableview.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from AnyQt.QtCore import Signal, QItemSelectionModel, Qt, QSize, QEvent
import io
import csv

from AnyQt.QtCore import Signal, QItemSelectionModel, Qt, QSize, QEvent, \
QByteArray, QMimeData
from AnyQt.QtGui import QMouseEvent
from AnyQt.QtWidgets import QTableView, QStyleOptionViewItem, QStyle

Expand Down Expand Up @@ -77,3 +81,44 @@ def changeEvent(self, event: QEvent) -> None:
if event.type() in (QEvent.StyleChange, QEvent.FontChange):
table_view_compact(self)
super().changeEvent(event)


def table_selection_to_mime_data(table):
"""Copy the current selection in a QTableView to the clipboard.
"""
lines = table_selection_to_list(table)

as_csv = lines_to_csv_string(lines, dialect="excel").encode("utf-8")
as_tsv = lines_to_csv_string(lines, dialect="excel-tab").encode("utf-8")

mime = QMimeData()
mime.setData("text/csv", QByteArray(as_csv))
mime.setData("text/tab-separated-values", QByteArray(as_tsv))
mime.setData("text/plain", QByteArray(as_tsv))
return mime


def lines_to_csv_string(lines, dialect="excel"):
stream = io.StringIO()
writer = csv.writer(stream, dialect=dialect)
writer.writerows(lines)
return stream.getvalue()


def table_selection_to_list(table):
model = table.model()
indexes = table.selectedIndexes()

rows = sorted(set(index.row() for index in indexes))
columns = sorted(set(index.column() for index in indexes))

lines = []
for row in rows:
line = []
for col in columns:
val = model.index(row, col).data(Qt.DisplayRole)
# TODO: use style item delegate displayText?
line.append("" if val is None else str(val))
lines.append(line)

return lines