Fix Issue #74: Add missing Grid.grid_y property

Added individual grid_x and grid_y getter properties to the Grid class
to complement the existing grid_size property. This allows direct access
to grid dimensions and fixes error messages that referenced these
properties before they existed.

closes #74
This commit is contained in:
John McCardle 2025-07-03 19:48:33 -04:00
parent 59e6f8d53d
commit f82b861bcd
15 changed files with 192 additions and 0 deletions

BIN
debug_immediate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
debug_multi_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
debug_multi_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
debug_multi_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
issue78_fixed_1658.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -269,6 +269,14 @@ PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) {
return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y);
} }
PyObject* UIGrid::get_grid_x(PyUIGridObject* self, void* closure) {
return PyLong_FromLong(self->data->grid_x);
}
PyObject* UIGrid::get_grid_y(PyUIGridObject* self, void* closure) {
return PyLong_FromLong(self->data->grid_y);
}
PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) { PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) {
auto& box = self->data->box; auto& box = self->data->box;
return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y);
@ -431,6 +439,8 @@ PyGetSetDef UIGrid::getsetters[] = {
// TODO - refactor into get_vector_member with field identifier values `(void*)n` // TODO - refactor into get_vector_member with field identifier values `(void*)n`
{"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, {"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL},
{"grid_x", (getter)UIGrid::get_grid_x, NULL, "Grid x dimension", NULL},
{"grid_y", (getter)UIGrid::get_grid_y, NULL, "Grid y dimension", NULL},
{"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL}, {"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL},
{"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL}, {"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL},
{"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, {"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL},

View File

@ -48,6 +48,8 @@ public:
static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds); static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds);
static PyObject* get_grid_size(PyUIGridObject* self, void* closure); static PyObject* get_grid_size(PyUIGridObject* self, void* closure);
static PyObject* get_grid_x(PyUIGridObject* self, void* closure);
static PyObject* get_grid_y(PyUIGridObject* self, void* closure);
static PyObject* get_position(PyUIGridObject* self, void* closure); static PyObject* get_position(PyUIGridObject* self, void* closure);
static int set_position(PyUIGridObject* self, PyObject* value, void* closure); static int set_position(PyUIGridObject* self, PyObject* value, void* closure);
static PyObject* get_size(PyUIGridObject* self, void* closure); static PyObject* get_size(PyUIGridObject* self, void* closure);

120
temp_test.py Normal file
View File

@ -0,0 +1,120 @@
import sys; sys.path.insert(0, '.')
#!/usr/bin/env python3
"""Test for UICollection - Related to issue #69 (Sequence Protocol)"""
import mcrfpy
from mcrfpy import automation
from datetime import datetime
def test_UICollection():
"""Test UICollection sequence protocol compliance"""
# Create test scene
mcrfpy.createScene("collection_test")
mcrfpy.setScene("collection_test")
ui = mcrfpy.sceneUI("collection_test")
# Add various UI elements
frame = mcrfpy.Frame(10, 10, 100, 100)
caption = mcrfpy.Caption(mcrfpy.Vector(120, 10), text="Test")
sprite = mcrfpy.Sprite(10, 120)
ui.append(frame)
ui.append(caption)
ui.append(sprite)
print("Testing UICollection sequence protocol (Issue #69)...")
# Test len()
try:
length = len(ui)
print(f"✓ len() works: {length} items")
except Exception as e:
print(f"✗ len() failed: {e}")
# Test indexing
try:
item0 = ui[0]
item1 = ui[1]
item2 = ui[2]
print(f"✓ Indexing works: [{type(item0).__name__}, {type(item1).__name__}, {type(item2).__name__}]")
# Test negative indexing
last_item = ui[-1]
print(f"✓ Negative indexing works: ui[-1] = {type(last_item).__name__}")
except Exception as e:
print(f"✗ Indexing failed: {e}")
# Test slicing (if implemented)
try:
slice_items = ui[0:2]
print(f"✓ Slicing works: got {len(slice_items)} items")
except Exception as e:
print(f"✗ Slicing not implemented (Issue #69): {e}")
# Test iteration
try:
types = []
for item in ui:
types.append(type(item).__name__)
print(f"✓ Iteration works: {types}")
except Exception as e:
print(f"✗ Iteration failed: {e}")
# Test contains
try:
if frame in ui:
print("'in' operator works")
else:
print("'in' operator returned False for existing item")
except Exception as e:
print(f"'in' operator not implemented (Issue #69): {e}")
# Test remove
try:
ui.remove(1) # Remove caption
print(f"✓ remove() works, now {len(ui)} items")
except Exception as e:
print(f"✗ remove() failed: {e}")
# Test type preservation (Issue #76)
try:
# Add a frame with children to test nested collections
parent_frame = mcrfpy.Frame(250, 10, 200, 200,
fill_color=mcrfpy.Color(200, 200, 200))
child_caption = mcrfpy.Caption(mcrfpy.Vector(10, 10), text="Child")
parent_frame.children.append(child_caption)
ui.append(parent_frame)
# Check if type is preserved when retrieving
retrieved = ui[-1]
if type(retrieved).__name__ == "Frame":
print("✓ Type preservation works")
else:
print(f"✗ Type not preserved (Issue #76): got {type(retrieved).__name__}")
except Exception as e:
print(f"✗ Type preservation test failed: {e}")
# Test find by name (Issue #41 - not yet implemented)
try:
found = ui.find("Test")
print(f"✓ find() method works: {type(found).__name__}")
except AttributeError:
print("✗ find() method not implemented (Issue #41)")
except Exception as e:
print(f"✗ find() method error: {e}")
# Take screenshot
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"test_UICollection_issue69_{timestamp}.png"
automation.screenshot(filename)
print(f"Screenshot saved: {filename}")
print("PASS")
# Set up timer to run test
mcrfpy.setTimer("test", test_UICollection, 1000)
# Cancel timer after running once
def cleanup():
mcrfpy.delTimer("test")
mcrfpy.delTimer("cleanup")
mcrfpy.setTimer("cleanup", cleanup, 1100)

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
Test for Issue #74: Add missing Grid.grid_y property
Verifies that Grid objects expose grid_x and grid_y properties correctly.
"""
def test_grid_xy_properties(timer_name):
"""Test that Grid has grid_x and grid_y properties"""
import mcrfpy
# Test was run
print("Issue #74 test: Grid.grid_x and Grid.grid_y properties")
# Test with texture
texture = mcrfpy.Texture("assets/kenney_ice.png", 16, 16)
grid = mcrfpy.Grid(20, 15, texture, (0, 0), (800, 600))
# Test grid_x property
assert hasattr(grid, 'grid_x'), "Grid should have grid_x property"
assert grid.grid_x == 20, f"Expected grid_x=20, got {grid.grid_x}"
print(f"✓ grid.grid_x = {grid.grid_x}")
# Test grid_y property
assert hasattr(grid, 'grid_y'), "Grid should have grid_y property"
assert grid.grid_y == 15, f"Expected grid_y=15, got {grid.grid_y}"
print(f"✓ grid.grid_y = {grid.grid_y}")
# Test grid_size still works
assert hasattr(grid, 'grid_size'), "Grid should still have grid_size property"
assert grid.grid_size == (20, 15), f"Expected grid_size=(20, 15), got {grid.grid_size}"
print(f"✓ grid.grid_size = {grid.grid_size}")
# Test without texture
grid2 = mcrfpy.Grid(30, 25, None, (10, 10), (480, 400))
assert grid2.grid_x == 30, f"Expected grid_x=30, got {grid2.grid_x}"
assert grid2.grid_y == 25, f"Expected grid_y=25, got {grid2.grid_y}"
assert grid2.grid_size == (30, 25), f"Expected grid_size=(30, 25), got {grid2.grid_size}"
print("✓ Grid without texture also has correct grid_x and grid_y")
# Test using in error message context (original issue)
try:
grid.at((-1, 0)) # Should raise error
except ValueError as e:
error_msg = str(e)
assert "Grid.grid_x" in error_msg, f"Error message should reference Grid.grid_x: {error_msg}"
print(f"✓ Error message correctly references Grid.grid_x: {error_msg}")
try:
grid.at((0, -1)) # Should raise error
except ValueError as e:
error_msg = str(e)
assert "Grid.grid_y" in error_msg, f"Error message should reference Grid.grid_y: {error_msg}"
print(f"✓ Error message correctly references Grid.grid_y: {error_msg}")
print("\n✅ Issue #74 test PASSED - Grid.grid_x and Grid.grid_y properties work correctly")
# Execute the test after a short delay to ensure window is ready
import mcrfpy
mcrfpy.setTimer("test_timer", test_grid_xy_properties, 100)

BIN
timer_success_1086.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB