Skip to content

Commit

Permalink
Merge pull request #5203 from VesnaT/testandscore_copy
Browse files Browse the repository at this point in the history
[ENH] Test and Score: Copy selected rows to clipboard
  • Loading branch information
janezd authored Jan 22, 2021
2 parents ef5a39c + b60fb4d commit 0d6da92
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 48 deletions.
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

0 comments on commit 0d6da92

Please sign in to comment.