Skip to content

Commit

Permalink
Widgets: Display a button to select entire row when hovering it
Browse files Browse the repository at this point in the history
  • Loading branch information
ccordoba12 committed Dec 30, 2024
1 parent 3f64ee3 commit 5213692
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 8 deletions.
85 changes: 82 additions & 3 deletions spyder/plugins/variableexplorer/widgets/collectionsdelegate.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,28 @@

# Third party imports
from qtpy.compat import to_qvariant
from qtpy.QtCore import QDateTime, QModelIndex, Qt, Signal
from qtpy.QtCore import (
QDateTime,
QEvent,
QModelIndex,
QRect,
QSize,
Qt,
Signal,
)
from qtpy.QtGui import QMouseEvent
from qtpy.QtWidgets import (
QAbstractItemDelegate, QDateEdit, QDateTimeEdit, QItemDelegate, QLineEdit,
QMessageBox, QTableView)
QAbstractItemDelegate,
QApplication,
QDateEdit,
QDateTimeEdit,
QItemDelegate,
QLineEdit,
QMessageBox,
QStyle,
QStyleOptionButton,
QTableView,
)
from spyder_kernels.utils.lazymodules import (
FakeObject, numpy as np, pandas as pd, PIL)
from spyder_kernels.utils.nsview import (display_to_value, is_editable_type,
Expand All @@ -33,10 +51,12 @@
from spyder.plugins.variableexplorer.widgets.dataframeeditor import (
DataFrameEditor)
from spyder.plugins.variableexplorer.widgets.texteditor import TextEditor
from spyder.utils.icon_manager import ima


LARGE_COLLECTION = 1e5
LARGE_ARRAY = 5e6
SELECT_ROW_BUTTON_SIZE = 22


class CollectionsDelegate(QItemDelegate, SpyderFontsMixin):
Expand Down Expand Up @@ -472,6 +492,65 @@ def updateEditorGeometry(self, editor, option, index):
super(CollectionsDelegate, self).updateEditorGeometry(
editor, option, index)

def paint(self, painter, option, index):
"""Actions to take when painting a cell."""
if index.column() == 3 and index.row() == self.parent().hovered_row:
# Paint regular contents
super().paint(painter, option, index)

# On the last column, and only for the hovered row, paint an extra
# button to select the entire row. This is necessary because in
# Spyder 6 is not intuitive how to do that since we use a single
# click to open the editor associated to the cell.
# Fixes spyder-ide/spyder#22524
# Solution adapted from https://stackoverflow.com/a/11778012/438386

# Getting the cell's rectangle
rect = option.rect

# Button left/top coordinates
x = rect.left() + rect.width() - SELECT_ROW_BUTTON_SIZE
y = rect.top() + rect.height() // 2 - SELECT_ROW_BUTTON_SIZE // 2

# Create and paint button
button = QStyleOptionButton()
button.rect = QRect(
x, y, SELECT_ROW_BUTTON_SIZE, SELECT_ROW_BUTTON_SIZE
)
button.text = ""
button.icon = ima.icon("select_row")
button.iconSize = QSize(20, 20)
button.state = QStyle.State_Enabled
QApplication.style().drawControl(
QStyle.CE_PushButtonLabel, button, painter
)
else:
super().paint(painter, option, index)

def editorEvent(self, event, model, option, index):
"""Actions to take when interacting with a cell."""
if event.type() == QEvent.MouseButtonRelease and index.column() == 3:
# Getting the position of the mouse click
click_x = QMouseEvent(event).x()
click_y = QMouseEvent(event).y()

# Getting the cell's rectangle
rect = option.rect

# Region for the select row button
x = rect.left() + rect.width() - SELECT_ROW_BUTTON_SIZE
y = rect.top()

# Select row when clicking on the button
if click_x > x and (y < click_y < (y + SELECT_ROW_BUTTON_SIZE)):
self.parent().selectRow(index.row())
else:
super().editorEvent(event, model, option, index)
else:
super().editorEvent(event, model, option, index)

return False


class ToggleColumnDelegate(CollectionsDelegate):
"""ToggleColumn Item Delegate"""
Expand Down
1 change: 1 addition & 0 deletions spyder/utils/icon_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def __init__(self):
'rename': [('mdi.rename-box',), {'color': self.MAIN_FG_COLOR}],
'move': [('mdi.file-move',), {'color': self.MAIN_FG_COLOR}],
'edit_add': [('mdi.plus-box',), {'color': self.MAIN_FG_COLOR}],
'select_row': [('mdi.plus-box-outline',), {'color': self.MAIN_FG_COLOR}],
'duplicate_row': [('ph.rows',), {'color': self.MAIN_FG_COLOR}],
'duplicate_column': [('ph.columns',), {'color': self.MAIN_FG_COLOR}],
'collapse_column': [('mdi.arrow-collapse-horizontal',), {'color': self.MAIN_FG_COLOR}],
Expand Down
53 changes: 48 additions & 5 deletions spyder/widgets/collectionseditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
import io
import re
import sys
import warnings
import textwrap
from typing import Any, Callable, Optional
import warnings

# Third party imports
from qtpy.compat import getsavefilename, to_qvariant
Expand Down Expand Up @@ -55,7 +56,9 @@
from spyder.utils.qthelpers import mimedata2url
from spyder.utils.stringmatching import get_search_scores, get_search_regex
from spyder.plugins.variableexplorer.widgets.collectionsdelegate import (
CollectionsDelegate)
CollectionsDelegate,
SELECT_ROW_BUTTON_SIZE,
)
from spyder.plugins.variableexplorer.widgets.importwizard import ImportWizard
from spyder.widgets.helperwidgets import CustomSortFilterProxy
from spyder.plugins.variableexplorer.widgets.basedialog import BaseDialog
Expand Down Expand Up @@ -479,6 +482,12 @@ def data(self, index, role=Qt.DisplayRole):
else:
display = value
if role == Qt.ToolTipRole:
if self.parent().over_select_row_button:
tooltip = _(
"Click to select this row. Maintain pressed Shift for "
"multiple rows"
)
return '\n'.join(textwrap.wrap(tooltip, 50))
return display
if role == Qt.UserRole:
if isinstance(value, NUMERIC_TYPES):
Expand Down Expand Up @@ -653,6 +662,7 @@ class BaseTableView(QTableView, SpyderWidgetMixin):
def __init__(self, parent):
super().__init__(parent=parent)

# Main attributes
self.array_filename = None
self.menu = None
self.empty_ws_menu = None
Expand All @@ -678,6 +688,8 @@ def __init__(self, parent):
self.source_model = None
self.setAcceptDrops(True)
self.automatic_column_width = True

# Headder attributes
self.setHorizontalHeader(BaseHeaderView(parent=self))
self.horizontalHeader().sig_user_resized_section.connect(
self.user_resize_columns)
Expand All @@ -698,6 +710,10 @@ def __init__(self, parent):
self._edit_value_timer.setSingleShot(True)
self._edit_value_timer.timeout.connect(self._edit_value)

# To paint the select row button and check if we are over it
self.hovered_row = -1
self.over_select_row_button = False

def setup_table(self):
"""Setup table"""
self.horizontalHeader().setStretchLastSection(True)
Expand Down Expand Up @@ -1037,9 +1053,16 @@ def set_data(self, data):
def _edit_value(self):
self.edit(self.__index_clicked)

def _update_hovered_row(self, event):
current_index = self.indexAt(event.pos())
if current_index.isValid():
self.hovered_row = current_index.row()
else:
self.hovered_row = -1

def mousePressEvent(self, event):
"""Reimplement Qt method"""
if event.button() != Qt.LeftButton:
if event.button() != Qt.LeftButton or self.over_select_row_button:
QTableView.mousePressEvent(self, event)
return

Expand Down Expand Up @@ -1070,9 +1093,19 @@ def mouseDoubleClickEvent(self, event):
pass

def mouseMoveEvent(self, event):
"""Change cursor shape."""
"""Actions to take when the mouse moves over the widget."""
self.over_select_row_button = False
self._update_hovered_row(event)

if self.rowAt(event.y()) != -1:
self.setCursor(Qt.PointingHandCursor)
select_row_button_width = SELECT_ROW_BUTTON_SIZE
if self.verticalScrollBar().isVisible():
select_row_button_width += self.verticalScrollBar().width()
if (self.width() - event.x()) < select_row_button_width:
self.over_select_row_button = True
self.setCursor(Qt.ArrowCursor)
else:
self.setCursor(Qt.PointingHandCursor)
else:
self.setCursor(Qt.ArrowCursor)

Expand Down Expand Up @@ -1124,6 +1157,16 @@ def dropEvent(self, event):
else:
event.ignore()

def leaveEvent(self, event):
"""Actions to take when the mouse leaves the widget."""
self.hovered_row = -1
super().leaveEvent(event)

def wheelEvent(self, event):
"""Actions to take on mouse wheel."""
self._update_hovered_row(event)
super().wheelEvent(event)

def showEvent(self, event):
"""Resize columns when the widget is shown."""
# This is probably the best we can do to adjust the columns width to
Expand Down

0 comments on commit 5213692

Please sign in to comment.