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

Improved copying and selection behaviour of array editor #2750

Merged
merged 2 commits into from
Oct 12, 2015
Merged
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
40 changes: 39 additions & 1 deletion spyderlib/widgets/varexp/arrayeditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
QMessageBox, QPushButton, QInputDialog, QMenu,
QApplication, QKeySequence, QLabel, QComboBox,
QSpinBox, QStackedWidget, QWidget, QVBoxLayout,
QAbstractItemDelegate)
QAbstractItemDelegate, QItemSelection,
QItemSelectionRange)
from spyderlib.qt.QtCore import (Qt, QModelIndex, QAbstractTableModel, Slot)

from spyderlib.qt.compat import to_qvariant, from_qvariant
Expand Down Expand Up @@ -403,10 +404,39 @@ def __init__(self, parent, model, dtype, shape):
lambda val: self.load_more_data(val, rows=True))

def load_more_data(self, value, rows=False, columns=False):
old_selection = self.selectionModel().selection()
old_rows_loaded = old_cols_loaded = None

if rows and value == self.verticalScrollBar().maximum():
old_rows_loaded = self.model().rows_loaded
self.model().fetch_more(rows=rows)

if columns and value == self.horizontalScrollBar().maximum():
old_cols_loaded = self.model().cols_loaded
self.model().fetch_more(columns=columns)

if old_rows_loaded is not None or old_cols_loaded is not None:
# if we've changed anything, update selection
new_selection = QItemSelection()
for part in old_selection:
top = part.top()
bottom = part.bottom()
if (old_rows_loaded is not None and
top == 0 and bottom == (old_rows_loaded-1)):
# complete column selected (so expand it to match updated range)
bottom = self.model().rows_loaded-1
left = part.left()
right = part.right()
if (old_cols_loaded is not None
and left == 0 and right == (old_cols_loaded-1)):
# compete row selected (so expand it to match updated range)
right = self.model().cols_loaded-1
top_left = self.model().index(top, left)
bottom_right = self.model().index(bottom, right)
part = QItemSelectionRange(top_left, bottom_right)
new_selection.append(part)
self.selectionModel().select(new_selection, self.selectionModel().ClearAndSelect)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one question here: this is done to extend the selection when more data is loaded, right?

But what happens if nothing is selected to start with?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - this is done to extend the selection when more data is loaded. If nothing is selected to start with nothing happens. The QItemSelection() constructor makes an empty selection, the for part in old_selection: loop is skipped and so one empty selection is replaced by another. I've have checked this case works and it does what you'd expect.

As a side note here - it is possible to build a selection where to whole row is selected but it doesn't get expanded (if you select a whole row, deselect a cell, reselect the cell selection ends up split into multiple parts). I don't think it's worth the effort to try to spot this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem :-) Great work!!


def resize_to_contents(self):
"""Resize cells to contents"""
Expand Down Expand Up @@ -444,6 +474,14 @@ def _sel_to_text(self, cell_range):
if not cell_range:
return
row_min, row_max, col_min, col_max = get_idx_rect(cell_range)
if col_min == 0 and col_max == (self.model().cols_loaded-1):
# we've selected a whole column. It isn't possible to
# select only the first part of a column without loading more,
# so we can treat it as intentional and copy the whole thing
col_max = self.model().total_cols-1
if row_min == 0 and row_max == (self.model().rows_loaded-1):
row_max = self.model().total_rows-1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this, it's very clever and solves the select columns, rows and all content in a very elegant way!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth mentioning that it doesn't quite do the right thing if you try to select an usual shaped region (i.e. select some rows or columns, then hold down ctrl and add a few extra cells elsewhere). That's an issue with the original code (not my added code) and it would probably be quite hard to work out what to do with numpy there.


_data = self.model().get_data()
if PY3:
output = io.BytesIO()
Expand Down