0
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2025-07-13 19:21:50 +02:00

feat(layout_manager): grid layout manager widget

This commit is contained in:
2024-11-19 14:07:37 +01:00
committed by wyzula_j
parent 66fc5306d6
commit 17a63e3b63
4 changed files with 1273 additions and 19 deletions

View File

@ -0,0 +1,368 @@
from typing import Optional
from unittest.mock import patch
import pytest
from qtpy.QtWidgets import QLabel, QPushButton, QWidget
from bec_widgets.widgets.containers.layout_manager.layout_manager import LayoutManagerWidget
class MockWidgetHandler:
def create_widget(self, widget_type: str) -> Optional[QWidget]:
if widget_type == "ButtonWidget":
return QPushButton()
elif widget_type == "LabelWidget":
return QLabel()
else:
return None
@pytest.fixture
def mock_widget_handler():
handler = MockWidgetHandler()
with patch(
"bec_widgets.widgets.containers.layout_manager.layout_manager.widget_handler", handler
):
yield handler
@pytest.fixture
def layout_manager(qtbot, mock_widget_handler):
widget = LayoutManagerWidget()
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
def test_add_widget_empty_position(layout_manager):
"""Test adding a widget to an empty position without shifting."""
btn1 = QPushButton("Button 1")
layout_manager.add_widget(btn1, row=0, col=0)
assert layout_manager.get_widget(0, 0) == btn1
assert layout_manager.widget_positions[btn1] == (0, 0, 1, 1)
assert layout_manager.position_widgets[(0, 0)] == btn1
def test_add_widget_occupied_position(layout_manager):
"""Test adding a widget to an occupied position with shifting (default direction right)."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=0, col=0) # This should shift btn1 to the right
assert layout_manager.get_widget(0, 0) == btn2
assert layout_manager.get_widget(0, 1) == btn1
assert layout_manager.widget_positions[btn2] == (0, 0, 1, 1)
assert layout_manager.widget_positions[btn1] == (0, 1, 1, 1)
def test_add_widget_directional_shift_down(layout_manager):
"""Test adding a widget to an occupied position but shifting down instead of right."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
btn3 = QPushButton("Button 3")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=0, col=0) # Shifts btn1 to the right by default
# Now add btn3 at (0,1) but shift direction is down, so it should push btn1 down.
layout_manager.add_widget(btn3, row=0, col=1, shift_direction="down")
assert layout_manager.get_widget(0, 0) == btn2
assert layout_manager.get_widget(0, 1) == btn3
assert layout_manager.get_widget(1, 1) == btn1
def test_remove_widget_by_position(layout_manager):
"""Test removing a widget by specifying its row and column."""
btn1 = QPushButton("Button 1")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.remove(row=0, col=0)
assert layout_manager.get_widget(0, 0) is None
assert btn1 not in layout_manager.widget_positions
def test_move_widget_with_shift(layout_manager):
"""Test moving a widget to an occupied position, triggering a shift."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
btn3 = QPushButton("Button 3")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=0, col=1)
layout_manager.add_widget(btn3, row=1, col=0)
layout_manager.move_widget(old_row=0, old_col=0, new_row=0, new_col=1, shift_direction="right")
assert layout_manager.get_widget(0, 1) == btn1
assert layout_manager.get_widget(0, 2) == btn2
assert layout_manager.get_widget(1, 0) == btn3
def test_move_widget_without_shift(layout_manager):
"""Test moving a widget to an occupied position without shifting."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=0, col=1)
with pytest.raises(ValueError) as exc_info:
layout_manager.move_widget(old_row=0, old_col=0, new_row=0, new_col=1, shift=False)
assert "Position (0, 1) is already occupied." in str(exc_info.value)
def test_change_layout_num_cols(layout_manager):
"""Test changing the layout by specifying only the number of columns."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
btn3 = QPushButton("Button 3")
btn4 = QPushButton("Button 4")
layout_manager.add_widget(btn1)
layout_manager.add_widget(btn2)
layout_manager.add_widget(btn3)
layout_manager.add_widget(btn4)
layout_manager.change_layout(num_cols=2)
assert layout_manager.get_widget(0, 0) == btn1
assert layout_manager.get_widget(0, 1) == btn2
assert layout_manager.get_widget(1, 0) == btn3
assert layout_manager.get_widget(1, 1) == btn4
def test_change_layout_num_rows(layout_manager):
"""Test changing the layout by specifying only the number of rows."""
btn_list = [QPushButton(f"Button {i}") for i in range(1, 7)]
for btn in btn_list:
layout_manager.add_widget(btn)
layout_manager.change_layout(num_rows=3)
assert layout_manager.get_widget(0, 0) == btn_list[0]
assert layout_manager.get_widget(0, 1) == btn_list[1]
assert layout_manager.get_widget(1, 0) == btn_list[2]
assert layout_manager.get_widget(1, 1) == btn_list[3]
assert layout_manager.get_widget(2, 0) == btn_list[4]
assert layout_manager.get_widget(2, 1) == btn_list[5]
def test_shift_all_widgets(layout_manager):
"""Test shifting all widgets down and then up."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=0, col=1)
# Shift all down
layout_manager.shift_all_widgets(direction="down")
assert layout_manager.get_widget(1, 0) == btn1
assert layout_manager.get_widget(1, 1) == btn2
# Shift all up
layout_manager.shift_all_widgets(direction="up")
assert layout_manager.get_widget(0, 0) == btn1
assert layout_manager.get_widget(0, 1) == btn2
def test_add_widget_auto_position(layout_manager):
"""Test adding widgets without specifying row and column."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1)
layout_manager.add_widget(btn2)
assert layout_manager.get_widget(0, 0) == btn1
assert layout_manager.get_widget(0, 1) == btn2
def test_clear_layout(layout_manager):
"""Test clearing the entire layout."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1)
layout_manager.add_widget(btn2)
layout_manager.clear_layout()
assert layout_manager.get_widget(0, 0) is None
assert layout_manager.get_widget(0, 1) is None
assert len(layout_manager.widget_positions) == 0
def test_add_widget_with_span(layout_manager):
"""Test adding a widget with rowspan and colspan."""
btn1 = QPushButton("Button 1")
layout_manager.add_widget(btn1, row=0, col=0, rowspan=2, colspan=2)
assert layout_manager.widget_positions[btn1] == (0, 0, 2, 2)
def test_add_widget_overlap_with_span(layout_manager):
"""
Test adding a widget that overlaps with an existing widget's span.
The code will attempt to shift widgets accordingly.
"""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1, row=0, col=0, rowspan=2, colspan=1)
layout_manager.add_widget(btn2, row=1, col=1, shift_direction="right")
assert layout_manager.get_widget(0, 0) == btn1
assert layout_manager.widget_positions[btn1] == (0, 0, 2, 1)
assert layout_manager.get_widget(1, 1) == btn2
assert layout_manager.widget_positions[btn2] == (1, 1, 1, 1)
@pytest.mark.parametrize(
"position, btn3_coords",
[("left", (1, 0)), ("right", (1, 2)), ("top", (0, 1)), ("bottom", (2, 1))],
)
def test_add_widget_relative(layout_manager, position, btn3_coords):
"""Test adding a widget relative to an existing widget using parameterized data."""
expected_row, expected_col = btn3_coords
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
btn3 = QPushButton("Button 3")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=1, col=1)
layout_manager.add_widget_relative(btn3, reference_widget=btn2, position=position)
assert layout_manager.get_widget(0, 0) == btn1
assert layout_manager.get_widget(1, 1) == btn2
assert layout_manager.get_widget(expected_row, expected_col) == btn3
def test_add_widget_relative_invalid_position(layout_manager):
"""Test adding a widget relative to an existing widget with an invalid position."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1, row=1, col=1)
with pytest.raises(ValueError) as exc_info:
layout_manager.add_widget_relative(btn2, reference_widget=btn1, position="invalid_position")
assert "Invalid position. Choose from 'left', 'right', 'top', 'bottom'." in str(exc_info.value)
btn2.deleteLater()
def test_add_widget_relative_to_nonexistent_widget(layout_manager):
"""Test adding a widget relative to a widget that does not exist in the layout."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
with pytest.raises(ValueError) as exc_info:
layout_manager.add_widget_relative(btn2, reference_widget=btn1, position="left")
assert "Reference widget not found in layout." in str(exc_info.value)
btn1.deleteLater()
btn2.deleteLater()
def test_add_widget_relative_with_shift(layout_manager):
"""Test adding a widget relative to an existing widget with shifting."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
btn3 = QPushButton("Button 3")
layout_manager.add_widget(btn1, row=1, col=1)
layout_manager.add_widget(btn2, row=1, col=0)
layout_manager.add_widget_relative(
btn3, reference_widget=btn1, position="left", shift_direction="right"
)
assert layout_manager.get_widget(0, 0) == btn3
assert layout_manager.get_widget(1, 1) == btn2
assert layout_manager.get_widget(0, 1) == btn1
def test_move_widget_by_object(layout_manager):
"""Test moving a widget using the widget object."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1)
layout_manager.add_widget(btn2, row=0, col=1)
layout_manager.move_widget_by_object(btn1, new_row=1, new_col=1)
# the grid is reindex after each move, so the new positions are (0,0) and (1,0), because visually there is only one column
assert layout_manager.get_widget(1, 0) == btn1
assert layout_manager.get_widget(0, 0) == btn2
def test_move_widget_by_coords(layout_manager):
"""Test moving a widget using its current coordinates."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1)
layout_manager.add_widget(btn2, row=0, col=1)
layout_manager.move_widget_by_coords(0, 0, 1, 0, shift_direction="down")
assert layout_manager.get_widget(1, 0) == btn1
assert layout_manager.get_widget(0, 1) == btn2
def test_change_layout_no_arguments(layout_manager):
"""Test changing the layout with no arguments (should do nothing)."""
btn1 = QPushButton("Button 1")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.change_layout()
assert layout_manager.get_widget(0, 0) == btn1
assert len(layout_manager.widget_positions) == 1
def test_remove_nonexistent_widget(layout_manager):
"""Test removing a widget that doesn't exist in the layout."""
with pytest.raises(ValueError) as exc_info:
layout_manager.remove(row=0, col=0)
assert "No widget found at position (0, 0)." in str(exc_info.value)
def test_reindex_grid_after_removal(layout_manager):
"""Test reindexing the grid after removing a widget."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1)
layout_manager.add_widget(btn2, row=0, col=1)
layout_manager.remove_widget(btn1)
layout_manager.reindex_grid()
# After removal and reindex, btn2 should shift to (0,0)
assert layout_manager.get_widget(0, 0) == btn2
assert layout_manager.widget_positions[btn2] == (0, 0, 1, 1)
def test_shift_all_widgets_up_at_top_row(layout_manager):
"""Test shifting all widgets up when they are already at the top row."""
btn1 = QPushButton("Button 1")
btn2 = QPushButton("Button 2")
layout_manager.add_widget(btn1, row=0, col=0)
layout_manager.add_widget(btn2, row=0, col=1)
# Shifting up should cause an error since widgets can't move above row 0
with pytest.raises(ValueError) as exc_info:
layout_manager.shift_all_widgets(direction="up")
assert "Shifting widgets out of grid boundaries." in str(exc_info.value)