feat(map): add batch export and adjust layout alignment
- Implement synchronous offscreen wafer map PNG rendering and CSV summary. - Add "Batch Export" button to file browser sidebar. - Adjust trend pane bottom spacer to align with About button when visible. - Fix type check errors and add pytest coverage for batch export.
This commit is contained in:
@@ -8,20 +8,6 @@ import pytest
|
||||
pytestmark = pytest.mark.usefixtures("qapp")
|
||||
|
||||
|
||||
def test_export_image_valid_path():
|
||||
"""export_image saves a PNG when grabToImage succeeds."""
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
|
||||
item = WaferMapItem()
|
||||
|
||||
mock_image = MagicMock()
|
||||
mock_result = MagicMock()
|
||||
mock_result.image.return_value = mock_image
|
||||
item.grabToImage = MagicMock(return_value=mock_result)
|
||||
|
||||
assert item.export_image("/tmp/test_wafer.png") is True
|
||||
|
||||
|
||||
def test_export_image_empty_path():
|
||||
"""export_image returns False for empty/None path."""
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
@@ -31,29 +17,23 @@ def test_export_image_empty_path():
|
||||
assert item.export_image(None) is False
|
||||
|
||||
|
||||
def test_export_image_grab_fails():
|
||||
"""export_image returns False when grabToImage raises."""
|
||||
def test_export_image_zero_size_item_fails():
|
||||
"""export_image returns False when the item has no geometry to render."""
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
|
||||
item = WaferMapItem()
|
||||
item.grabToImage = MagicMock(side_effect=RuntimeError("grab failed"))
|
||||
|
||||
assert item.export_image("/tmp/test_wafer.png") is False
|
||||
|
||||
|
||||
def test_export_image_save_fails():
|
||||
"""export_image returns False when image.save raises."""
|
||||
def test_export_image_unwritable_path_fails(tmp_path):
|
||||
"""export_image returns False when the target directory doesn't exist."""
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
|
||||
mock_image = MagicMock()
|
||||
mock_image.save.side_effect = IOError("disk full")
|
||||
mock_result = MagicMock()
|
||||
mock_result.image.return_value = mock_image
|
||||
|
||||
item = WaferMapItem()
|
||||
item.grabToImage = MagicMock(return_value=mock_result)
|
||||
|
||||
assert item.export_image("/tmp/test_wafer.png") is False
|
||||
item.setWidth(100)
|
||||
item.setHeight(100)
|
||||
item.values = [10.0, 20.0, 30.0]
|
||||
assert item.export_image(str(tmp_path / "no_such_dir" / "wafer.png")) is False
|
||||
|
||||
|
||||
def test_thickness_properties():
|
||||
@@ -88,3 +68,57 @@ def test_show_thickness_setter():
|
||||
|
||||
item.showThickness = True
|
||||
assert item._show_thickness is True
|
||||
|
||||
|
||||
def test_export_image_writes_real_png_headless(tmp_path):
|
||||
"""export_image must produce an actual decodable image file without a
|
||||
QQuickWindow — the button was silently failing because grabToImage()
|
||||
resolves asynchronously and never completes headless/immediately."""
|
||||
from PySide6.QtGui import QImage
|
||||
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
|
||||
item = WaferMapItem()
|
||||
item.setWidth(200)
|
||||
item.setHeight(200)
|
||||
item.values = [10.0, 20.0, 30.0]
|
||||
out = tmp_path / "wafer.png"
|
||||
|
||||
assert item.export_image(str(out)) is True
|
||||
assert out.exists()
|
||||
loaded = QImage(str(out))
|
||||
assert not loaded.isNull()
|
||||
assert loaded.width() == 200
|
||||
|
||||
|
||||
def test_export_image_includes_metrics_footer(tmp_path):
|
||||
"""With sensor values loaded, the export gains a footer strip below the
|
||||
map carrying the metric readouts (sensors / min / max / avg)."""
|
||||
from PySide6.QtGui import QImage
|
||||
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
|
||||
item = WaferMapItem()
|
||||
item.setWidth(200)
|
||||
item.setHeight(200)
|
||||
item.values = [10.0, 20.0, 30.0]
|
||||
out = tmp_path / "wafer.png"
|
||||
|
||||
assert item.export_image(str(out)) is True
|
||||
loaded = QImage(str(out))
|
||||
assert loaded.height() > 200 # footer strip appended
|
||||
# footer is painted opaque (dark bg), not transparent
|
||||
assert loaded.pixelColor(5, loaded.height() - 5).alpha() == 255
|
||||
|
||||
|
||||
def test_export_image_no_data_fails(tmp_path):
|
||||
"""No file loaded / no stream played -> nothing meaningful to export."""
|
||||
from pygui.backend.visualization.wafer_map_item import WaferMapItem
|
||||
|
||||
item = WaferMapItem()
|
||||
item.setWidth(200)
|
||||
item.setHeight(200)
|
||||
out = tmp_path / "wafer.png"
|
||||
|
||||
assert item.export_image(str(out)) is False
|
||||
assert not out.exists()
|
||||
|
||||
Reference in New Issue
Block a user