You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
655 lines
22 KiB
655 lines
22 KiB
#!/usr/bin/env python3 |
|
# MIT License |
|
# |
|
# Copyright (c) 2025 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
|
# |
|
# Permission is hereby granted, free of charge, to any person obtaining a copy |
|
# of this software and associated documentation files (the "Software"), to deal |
|
# in the Software without restriction, including without limitation the rights |
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
# copies of the Software, and to permit persons to whom the Software is |
|
# furnished to do so, subject to the following conditions: |
|
# |
|
# The above copyright notice and this permission notice shall be included in all |
|
# copies or substantial portions of the Software. |
|
# |
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
# SOFTWARE. |
|
|
|
import os |
|
import sys |
|
import pytest |
|
import io |
|
from unittest.mock import Mock, MagicMock, patch |
|
|
|
sys.path.append(os.path.join(sys.path[0],'../../../libs/pyTermTk')) |
|
import TermTk as ttk |
|
from TermTk.TTkWidgets.TTkModelView.tablewidget import _DragPosType, _ClipboardTable, _SelectionProxy, TTkHeaderView |
|
|
|
class TestTTkHeaderView: |
|
"""Test cases for TTkHeaderView class""" |
|
|
|
def test_init_default(self): |
|
"""Test default initialization""" |
|
header = TTkHeaderView() |
|
assert header.isVisible() == True |
|
assert hasattr(header, 'visibilityUpdated') |
|
|
|
def test_init_with_visibility(self): |
|
"""Test initialization with specific visibility""" |
|
header_visible = TTkHeaderView(visible=True) |
|
header_hidden = TTkHeaderView(visible=False) |
|
|
|
assert header_visible.isVisible() == True |
|
assert header_hidden.isVisible() == False |
|
|
|
def test_set_visible(self): |
|
"""Test setVisible method""" |
|
header = TTkHeaderView() |
|
|
|
header.setVisible(False) |
|
assert header.isVisible() == False |
|
|
|
header.setVisible(True) |
|
assert header.isVisible() == True |
|
|
|
def test_show_hide(self): |
|
"""Test show and hide methods""" |
|
header = TTkHeaderView(visible=False) |
|
|
|
header.show() |
|
assert header.isVisible() == True |
|
|
|
header.hide() |
|
assert header.isVisible() == False |
|
|
|
def test_visibility_signal(self): |
|
"""Test visibility signal emission""" |
|
header = TTkHeaderView() |
|
signal_calls = [] |
|
|
|
@ttk.pyTTkSlot(bool) |
|
def mock_slot(visible): |
|
signal_calls.append(visible) |
|
|
|
header.visibilityUpdated.connect(mock_slot) |
|
|
|
header.hide() |
|
assert len(signal_calls) == 1 |
|
assert signal_calls[0] == False |
|
|
|
header.show() |
|
assert len(signal_calls) == 2 |
|
assert signal_calls[1] == True |
|
|
|
class TestClipboardTable: |
|
"""Test cases for _ClipboardTable class""" |
|
|
|
def test_init_empty(self): |
|
"""Test initialization with empty data""" |
|
clipboard = _ClipboardTable([]) |
|
assert clipboard.data() == [] |
|
assert isinstance(clipboard, ttk.TTkString) |
|
|
|
def test_init_with_data(self): |
|
"""Test initialization with data""" |
|
data = [ |
|
[(0, 0, 'A1'), (0, 1, 'B1')], |
|
[(1, 0, 'A2'), (1, 1, 'B2')] |
|
] |
|
clipboard = _ClipboardTable(data) |
|
|
|
assert clipboard.data() == data |
|
assert isinstance(clipboard, ttk.TTkString) |
|
assert len(str(clipboard)) > 0 |
|
|
|
def test_string_conversion(self): |
|
"""Test TTkString conversion""" |
|
data = [ |
|
[(0, 0, 'Cell1'), (0, 1, 'Cell2')], |
|
[(1, 0, 'Cell3'), (1, 1, 'Cell4')] |
|
] |
|
clipboard = _ClipboardTable(data) |
|
|
|
string_repr = str(clipboard) |
|
# Should contain the cell data in some formatted way |
|
assert 'Cell1' in string_repr or str(clipboard).find('Cell') >= 0 |
|
|
|
class TestTTkTableWidget: |
|
"""Test cases for TTkTableWidget class""" |
|
|
|
def setup_method(self): |
|
"""Set up test fixtures before each test method.""" |
|
self.test_data = [ |
|
['Alice', 25, 'Engineer'], |
|
['Bob', 30, 'Designer'], |
|
['Charlie', 35, 'Manager'] |
|
] |
|
self.header = ['Name', 'Age', 'Role'] |
|
self.indexes = ['Row1', 'Row2', 'Row3'] |
|
|
|
class MockTableModel(ttk.TTkTableModelList): |
|
def flags(self, row, col): |
|
return super().flags(row, col) |
|
|
|
# Create a basic table model for testing |
|
self.table_model = MockTableModel( |
|
data=self.test_data, |
|
header=self.header, |
|
indexes=self.indexes |
|
) |
|
|
|
def test_init_default(self): |
|
"""Test default initialization""" |
|
widget = ttk.TTkTableWidget() |
|
|
|
assert widget is not None |
|
assert widget.model() is not None |
|
assert isinstance(widget.model(), ttk.TTkAbstractTableModel) |
|
# Default model should have 10x10 grid with empty strings |
|
assert widget.rowCount() == 10 |
|
assert widget.columnCount() == 10 |
|
|
|
def test_init_with_model(self): |
|
"""Test initialization with a table model""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
assert widget.model() == self.table_model |
|
assert widget.rowCount() == 3 |
|
assert widget.columnCount() == 3 |
|
|
|
def test_init_parameters(self): |
|
"""Test initialization with all parameters""" |
|
widget = ttk.TTkTableWidget( |
|
tableModel=self.table_model, |
|
vSeparator=False, |
|
hSeparator=False, |
|
vHeader=False, |
|
hHeader=False, |
|
sortingEnabled=True, |
|
dataPadding=3 |
|
) |
|
|
|
assert widget.model() == self.table_model |
|
assert not widget.vSeparatorVisibility() |
|
assert not widget.hSeparatorVisibility() |
|
assert not widget.verticalHeader().isVisible() |
|
assert not widget.horizontalHeader().isVisible() |
|
assert widget.isSortingEnabled() |
|
assert widget._dataPadding == 3 |
|
|
|
def test_header_views(self): |
|
"""Test header view functionality""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
v_header = widget.verticalHeader() |
|
h_header = widget.horizontalHeader() |
|
|
|
assert isinstance(v_header, TTkHeaderView) |
|
assert isinstance(h_header, TTkHeaderView) |
|
assert v_header.isVisible() |
|
assert h_header.isVisible() |
|
|
|
# Test header visibility changes trigger layout refresh |
|
with patch.object(widget, '_headerVisibilityChanged') as mock_refresh: |
|
v_header.hide() |
|
# Signal should have been emitted and connected |
|
assert not v_header.isVisible() |
|
|
|
def test_model_management(self): |
|
"""Test model getter/setter and related functionality""" |
|
widget = ttk.TTkTableWidget() |
|
original_model = widget.model() |
|
|
|
# Test model change |
|
widget.setModel(self.table_model) |
|
assert widget.model() == self.table_model |
|
assert widget.model() != original_model |
|
|
|
# Test that model signals are connected |
|
assert widget.model().dataChanged._connected_slots |
|
assert widget.model().modelChanged._connected_slots |
|
|
|
# Test model change triggers layout refresh |
|
with patch.object(widget, '_refreshLayout') as mock_refresh: |
|
new_model = ttk.TTkTableModelList(data=[['test']]) |
|
widget.setModel(new_model) |
|
# _refreshLayout should be called when model changes |
|
# Note: This depends on the modelChanged signal being emitted |
|
|
|
def test_selection_proxy_integration(self): |
|
"""Test selection proxy integration""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
proxy = widget._select_proxy |
|
assert isinstance(proxy, _SelectionProxy) |
|
assert proxy._rows == 3 |
|
assert proxy._cols == 3 |
|
|
|
# Test selection operations update proxy |
|
widget.selectRow(1) |
|
assert proxy.isRowSelected(1) |
|
|
|
widget.selectColumn(2) |
|
assert proxy.isColSelected(2) |
|
|
|
widget.clearSelection() |
|
assert not proxy.isRowSelected(1) |
|
assert not proxy.isColSelected(2) |
|
|
|
def test_current_cell_management(self): |
|
"""Test current cell position management""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Initially no current position |
|
assert widget._currentPos is None |
|
assert widget.currentRow() == 0 # Default |
|
assert widget.currentColumn() == 0 # Default |
|
|
|
# Test setting current cell |
|
widget._setCurrentCell(1, 2) |
|
assert widget._currentPos == (1, 2) |
|
assert widget.currentRow() == 1 |
|
assert widget.currentColumn() == 2 |
|
|
|
# # Test movement |
|
# widget._moveCurrentCell(row=1, col=1) |
|
# assert widget._currentPos == (2, 3) # Should be (1+1, 2+1) if movement works |
|
|
|
# Test boundary handling |
|
widget._moveCurrentCell(row=5, col=5) # Should clamp to table bounds |
|
assert widget._currentPos[0] < widget.rowCount() |
|
assert widget._currentPos[1] < widget.columnCount() |
|
|
|
def test_snapshot_system(self): |
|
"""Test undo/redo snapshot system""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Initially no snapshots |
|
assert len(widget._snapshot) == 0 |
|
assert widget._snapshotId == 0 |
|
assert not widget.isUndoAvailable() |
|
assert not widget.isRedoAvailable() |
|
|
|
# Create a snapshot by modifying data |
|
original_value = widget.model().data(0, 0) |
|
widget._tableModel_setData([(0, 0, 'NewValue')]) |
|
|
|
# Should have a snapshot now |
|
assert widget.isUndoAvailable() |
|
assert not widget.isRedoAvailable() |
|
assert widget.model().data(0, 0) == 'NewValue' |
|
|
|
# Test undo |
|
widget.undo() |
|
assert widget.model().data(0, 0) == original_value |
|
assert not widget.isUndoAvailable() |
|
assert widget.isRedoAvailable() |
|
|
|
# Test redo |
|
widget.redo() |
|
assert widget.model().data(0, 0) == 'NewValue' |
|
assert widget.isUndoAvailable() |
|
assert not widget.isRedoAvailable() |
|
|
|
def test_clipboard_operations(self): |
|
"""Test clipboard operations""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Select some cells |
|
widget.setSelection((0, 0), (2, 2), ttk.TTkK.TTkItemSelectionModel.Select) |
|
|
|
# Test copy |
|
widget.copy() |
|
# Should not raise exception |
|
|
|
# Test cut |
|
original_values = [ |
|
[widget.model().data(r, c) for c in range(2)] |
|
for r in range(2) |
|
] |
|
widget.cut() |
|
|
|
# Values should be cleared after cut |
|
for r in range(2): |
|
for c in range(2): |
|
if widget._select_proxy.isCellSelected(c, r): |
|
assert widget.model().data(r, c) == '' |
|
|
|
# Test paste |
|
widget.paste() |
|
# Should restore some values or handle gracefully |
|
|
|
def test_sorting_functionality(self): |
|
"""Test sorting functionality""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Initially disabled |
|
assert not widget.isSortingEnabled() |
|
|
|
# Enable sorting |
|
widget.setSortingEnabled(True) |
|
assert widget.isSortingEnabled() |
|
|
|
# Test sorting by column |
|
original_first_name = widget.model().data(0, 0) |
|
widget.sortByColumn(0, ttk.TTkK.SortOrder.AscendingOrder) |
|
|
|
# Can't easily test actual sorting without knowing internal implementation |
|
# but should not raise exception |
|
|
|
# Test descending sort |
|
widget.sortByColumn(0, ttk.TTkK.SortOrder.DescendingOrder) |
|
|
|
def test_resize_operations(self): |
|
"""Test column and row resize operations""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Test individual operations |
|
widget.setColumnWidth(0, 20) |
|
widget.setRowHeight(0, 3) |
|
|
|
# Test resize to contents |
|
widget.resizeColumnToContents(0) |
|
widget.resizeRowToContents(0) |
|
|
|
# Test resize all to contents |
|
widget.resizeColumnsToContents() |
|
widget.resizeRowsToContents() |
|
|
|
# These should not raise exceptions |
|
|
|
def test_view_area_calculation(self): |
|
"""Test view area size calculation""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# With headers |
|
width, height = widget.viewFullAreaSize() |
|
assert width > 0 |
|
assert height > 0 |
|
|
|
# Without headers |
|
widget.verticalHeader().hide() |
|
widget.horizontalHeader().hide() |
|
|
|
width_no_headers, height_no_headers = widget.viewFullAreaSize() |
|
assert width_no_headers <= width |
|
assert height_no_headers <= height |
|
|
|
def test_find_cell_functionality(self): |
|
"""Test _findCell coordinate conversion""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Test with headers |
|
row, col = widget._findCell(10, 5, headers=True) |
|
assert isinstance(row, int) |
|
assert isinstance(col, int) |
|
assert row >= -1 # -1 for header, >= 0 for cells |
|
assert col >= -1 # -1 for header, >= 0 for cells |
|
|
|
# Test without headers |
|
row, col = widget._findCell(10, 5, headers=False) |
|
assert isinstance(row, int) |
|
assert isinstance(col, int) |
|
assert row >= 0 |
|
assert col >= 0 |
|
|
|
def test_mouse_events(self): |
|
"""Test mouse event handling""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Mock mouse events |
|
press_event = Mock() |
|
press_event.x = 15 |
|
press_event.y = 2 |
|
press_event.mod = ttk.TTkK.NoModifier |
|
|
|
move_event = Mock() |
|
move_event.x = 20 |
|
move_event.y = 3 |
|
move_event.mod = ttk.TTkK.NoModifier |
|
|
|
release_event = Mock() |
|
release_event.x = 20 |
|
release_event.y = 3 |
|
release_event.mod = ttk.TTkK.NoModifier |
|
|
|
double_click_event = Mock() |
|
double_click_event.x = 15 |
|
double_click_event.y = 2 |
|
double_click_event.mod = ttk.TTkK.NoModifier |
|
|
|
# Test event handling (should not crash) |
|
with patch.object(widget, '_findCell', return_value=(1, 1)): |
|
assert widget.mousePressEvent(press_event) == True |
|
assert widget.mouseMoveEvent(move_event) == True |
|
assert widget.mouseReleaseEvent(release_event) == True |
|
assert widget.mouseDoubleClickEvent(double_click_event) == True |
|
|
|
def test_keyboard_events(self): |
|
"""Test keyboard event handling""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Set initial position |
|
widget._setCurrentCell(1, 1) |
|
|
|
# Mock keyboard events |
|
key_events = [ |
|
Mock(key=ttk.TTkK.Key_Up, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Down, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Left, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Right, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Tab, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Backtab, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Enter, mod=ttk.TTkK.NoModifier), |
|
Mock(key=ttk.TTkK.Key_Space, mod=ttk.TTkK.NoModifier), |
|
] |
|
|
|
for event in key_events: |
|
# Should handle keyboard navigation |
|
result = widget.keyEvent(event) |
|
# Result depends on implementation, but should not crash |
|
|
|
def test_signal_emissions(self): |
|
"""Test signal emissions""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Connect signals to mock slots |
|
cell_changed_calls = [] |
|
cell_clicked_calls = [] |
|
cell_entered_calls = [] |
|
current_cell_changed_calls = [] |
|
|
|
@ttk.pyTTkSlot(int, int) |
|
def mock_cell_changed(row, col): |
|
cell_changed_calls.append((row, col)) |
|
|
|
@ttk.pyTTkSlot(int, int) |
|
def mock_cell_clicked(row, col): |
|
cell_clicked_calls.append((row, col)) |
|
|
|
@ttk.pyTTkSlot(int, int) |
|
def mock_cell_entered(row, col): |
|
cell_entered_calls.append((row, col)) |
|
|
|
@ttk.pyTTkSlot(int, int, int, int) |
|
def mock_current_cell_changed(curr_row, curr_col, prev_row, prev_col): |
|
current_cell_changed_calls.append((curr_row, curr_col, prev_row, prev_col)) |
|
|
|
widget.cellChanged.connect(mock_cell_changed) |
|
widget.cellClicked.connect(mock_cell_clicked) |
|
widget.cellEntered.connect(mock_cell_entered) |
|
widget.currentCellChanged.connect(mock_current_cell_changed) |
|
|
|
# Trigger events that should emit signals |
|
widget._cellChanged.emit(1, 2) |
|
widget._cellClicked.emit(0, 1) |
|
widget._cellEntered.emit(2, 0) |
|
widget._currentCellChanged.emit(1, 1, 0, 0) |
|
|
|
# Verify signals were received |
|
assert len(cell_changed_calls) == 1 |
|
assert cell_changed_calls[0] == (1, 2) |
|
assert len(cell_clicked_calls) == 1 |
|
assert cell_clicked_calls[0] == (0, 1) |
|
assert len(cell_entered_calls) == 1 |
|
assert cell_entered_calls[0] == (2, 0) |
|
assert len(current_cell_changed_calls) == 1 |
|
assert current_cell_changed_calls[0] == (1, 1, 0, 0) |
|
|
|
def test_edge_cases(self): |
|
"""Test edge cases and boundary conditions""" |
|
# Empty model |
|
empty_model = ttk.TTkTableModelList(data=[], header=[]) |
|
widget = ttk.TTkTableWidget(tableModel=empty_model) |
|
|
|
assert widget.rowCount() == 0 |
|
assert widget.columnCount() == 0 |
|
|
|
# Operations should handle empty model gracefully |
|
widget.selectAll() |
|
widget.clearSelection() |
|
widget.resizeColumnsToContents() |
|
widget.resizeRowsToContents() |
|
widget.copy() |
|
widget.cut() |
|
widget.paste() |
|
|
|
# Single cell model |
|
single_model = ttk.TTkTableModelList(data=[['Cell']], header=['Col']) |
|
widget.setModel(single_model) |
|
|
|
assert widget.rowCount() == 1 |
|
assert widget.columnCount() == 1 |
|
|
|
widget.selectAll() |
|
widget._setCurrentCell(0, 0) |
|
|
|
# Large model stress test |
|
large_data = [[f'Cell_{i}_{j}' for j in range(50)] for i in range(50)] |
|
large_model = ttk.TTkTableModelList(data=large_data) |
|
widget.setModel(large_model) |
|
|
|
assert widget.rowCount() == 50 |
|
assert widget.columnCount() == 50 |
|
|
|
# Should handle large models without issues |
|
widget.selectRow(25) |
|
widget.selectColumn(25) |
|
|
|
def test_focus_and_events(self): |
|
"""Test focus handling and event processing""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Test focus policy |
|
focus_policy = widget.focusPolicy() |
|
assert focus_policy & ttk.TTkK.ClickFocus |
|
assert focus_policy & ttk.TTkK.TabFocus |
|
|
|
# Test focus events |
|
widget.focusOutEvent() # Should not crash |
|
|
|
# Test leave event |
|
leave_event = Mock() |
|
leave_event.x = 100 |
|
leave_event.y = 100 |
|
result = widget.leaveEvent(leave_event) |
|
# Should handle gracefully |
|
|
|
def test_internal_state_consistency(self): |
|
"""Test internal state consistency""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Test that internal positions are consistent |
|
widget._setCurrentCell(1, 2) |
|
assert widget._currentPos == (1, 2) |
|
assert widget.currentRow() == 1 |
|
assert widget.currentColumn() == 2 |
|
|
|
# Test drag position initialization |
|
assert widget._dragPos is None |
|
assert widget._hoverPos is None |
|
assert widget._hSeparatorSelected is None |
|
assert widget._vSeparatorSelected is None |
|
|
|
# Test selection proxy consistency |
|
proxy = widget._select_proxy |
|
assert proxy._rows == widget.rowCount() |
|
assert proxy._cols == widget.columnCount() |
|
|
|
def test_style_and_appearance(self): |
|
"""Test style properties""" |
|
widget = ttk.TTkTableWidget() |
|
|
|
# Test class style exists |
|
assert hasattr(ttk.TTkTableWidget, 'classStyle') |
|
assert isinstance(ttk.TTkTableWidget.classStyle, dict) |
|
assert 'default' in ttk.TTkTableWidget.classStyle |
|
assert 'disabled' in ttk.TTkTableWidget.classStyle |
|
|
|
# Test style properties contain expected keys |
|
default_style = ttk.TTkTableWidget.classStyle['default'] |
|
expected_keys = ['color', 'lineColor', 'headerColor', 'hoverColor', |
|
'currentColor', 'selectedColor', 'separatorColor'] |
|
for key in expected_keys: |
|
assert key in default_style |
|
assert isinstance(default_style[key], ttk.TTkColor) |
|
|
|
def test_paste_event_handling(self): |
|
"""Test paste event with different data types""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
widget._setCurrentCell(0, 0) |
|
|
|
# Test paste with TTkString |
|
text_data = ttk.TTkString("Hello\tWorld\nFoo\tBar") |
|
result = widget.pasteEvent(text_data) |
|
assert result == True |
|
|
|
# Test paste with _ClipboardTable |
|
clipboard_data = [ |
|
[(0, 0, 'A1'), (0, 1, 'B1')], |
|
[(1, 0, 'A2'), (1, 1, 'B2')] |
|
] |
|
clipboard_table = _ClipboardTable(clipboard_data) |
|
result = widget.pasteEvent(clipboard_table) |
|
assert result == True |
|
|
|
# Test paste with unsupported data |
|
result = widget.pasteEvent("regular string") |
|
assert result == True # Should handle gracefully |
|
|
|
def test_model_flags_integration(self): |
|
"""Test integration with model flags""" |
|
widget = ttk.TTkTableWidget(tableModel=self.table_model) |
|
|
|
# Mock model flags |
|
def mock_flags(row, col): |
|
if row == 1 and col == 1: |
|
return ttk.TTkK.ItemFlag.NoItemFlags # Not selectable |
|
return ttk.TTkK.ItemFlag.ItemIsSelectable | ttk.TTkK.ItemFlag.ItemIsEnabled |
|
|
|
original_flags = widget._tableModel.flags |
|
widget._tableModel.flags = mock_flags |
|
|
|
try: |
|
# Test selection respects flags |
|
widget._select_proxy.updateModel( |
|
cols=widget.columnCount(), |
|
rows=widget.rowCount(), |
|
flags=widget._tableModel.flags |
|
) |
|
|
|
widget.selectAll() |
|
# Cell (1,1) should not be selected due to flags |
|
assert not widget._select_proxy.isCellSelected(1, 1) |
|
# Other cells should be selected |
|
assert widget._select_proxy.isCellSelected(0, 0) |
|
assert widget._select_proxy.isCellSelected(2, 2) |
|
|
|
finally: |
|
widget._tableModel.flags = original_flags
|
|
|