Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support changing images in ImageEditor for all IImage subclasses #1810

Merged
merged 7 commits into from
Feb 18, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/releases/upcoming/1810.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix issue with ImageEditor not updating for all IImage implementations.
3 changes: 1 addition & 2 deletions traitsui/qt4/image_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,7 @@ def update_editor(self):
"""
if self.factory.image is None:
value = self.value
if isinstance(value, ImageResource):
self.control.setPixmap(convert_bitmap(value))
self.control.setPixmap(convert_bitmap(value))
self.control.setScaledContents(self.factory.scale)
self.control.setAllowUpscaling(self.factory.allow_upscaling)
self.control.setPreserveAspectRatio(self.factory.preserve_aspect_ratio)
Expand Down
142 changes: 142 additions & 0 deletions traitsui/tests/editors/test_image_editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# (C) Copyright 2004-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Tests pertaining to the ImageEditor
"""

import unittest

import pkg_resources

from pyface.api import Image, ImageResource
from traits.api import File, HasTraits
from traitsui.api import ImageEditor, Item, View
from traitsui.tests._tools import (
BaseTestMixin,
create_ui,
requires_toolkit,
ToolkitName,
)


filename1 = pkg_resources.resource_filename(
"traitsui", "examples/demo/Extras/images/e-logo-rev.png"
)
filename2 = pkg_resources.resource_filename(
"traitsui", "examples/demo/Extras/images/info.png"
)


class ImageDisplay(HasTraits):

image = Image()


@requires_toolkit([ToolkitName.wx])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can't these be run on Qt?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI currently only runs on Qt.

Locally I ran python etstool.py install --toolkit=wx, edm shell -e traitsui-test-3.6-wx and python -m unittest traitsui.tests.editors.test_image_editor and I see the following test failures:

======================================================================
ERROR: test_image_editor_none (traitsui.tests.editors.test_image_editor.TestImageEditor)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/tests/editors/test_image_editor.py", line 141, in test_image_editor_none
    with create_ui(obj1, dict(view=view)) as ui:
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-wx/lib/python3.6/contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/testing/tester/ui_tester.py", line 105, in create_ui
    ui = object.edit_traits(**ui_kwargs)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-wx/lib/python3.6/site-packages/traits/has_traits.py", line 1830, in edit_traits
    args,
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/view.py", line 450, in ui
    ui.ui(parent, kind)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/ui.py", line 235, in ui
    self.rebuild(self, parent)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/toolkit.py", line 118, in ui_live
    ui_live.ui_live(ui, parent)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_live.py", line 47, in ui_live
    _ui_dialog(ui, parent, BaseDialog.NONMODAL)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_live.py", line 81, in _ui_dialog
    BaseDialog.display_ui(ui, parent, style)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_base.py", line 61, in display_ui
    ui.owner.init(ui, parent, style)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_live.py", line 195, in init
    sw = panel(ui, window)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_panel.py", line 266, in panel
    panel, content[0], ui
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_panel.py", line 423, in fill_panel_for_group
    panel, group, ui, suppress_label, is_dock_window, create_panel
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_panel.py", line 577, in __init__
    self.add_items(content, panel, self.sizer)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/ui_panel.py", line 932, in add_items
    editor.prepare(item_panel)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/editor.py", line 247, in prepare
    self.init(parent)
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/wx/image_editor.py", line 44, in init
    self.control = ImageControl(parent, convert_bitmap(image), padding=0)
  File "/Users/aayres/.edm/envs/traitsui-test-3.6-wx/lib/python3.6/site-packages/pyface/ui_traits.py", line 92, in convert_bitmap
    return image.create_bitmap()
AttributeError: 'NoneType' object has no attribute 'create_bitmap'

======================================================================
ERROR: test_image_editor_resource (traitsui.tests.editors.test_image_editor.TestImageEditor)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/tests/editors/test_image_editor.py", line 62, in test_image_editor_resource
    image=ImageResource(absolute_path=filename1)
TypeError: __init__() got an unexpected keyword argument 'absolute_path'

======================================================================
ERROR: test_image_editor_static (traitsui.tests.editors.test_image_editor.TestImageEditor)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/aayres/Desktop/all_ets/traitsui/traitsui/tests/editors/test_image_editor.py", line 51, in test_image_editor_static
    image=ImageResource(absolute_path=filename1),
TypeError: __init__() got an unexpected keyword argument 'absolute_path'

----------------------------------------------------------------------
Ran 5 tests in 1.216s

FAILED (errors=3)

running with pyside2 I see the same failures. In pyface.i_image_resource.MImageResource the __init__ method is overridden and doesn't call super.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should now be resolved. The absolute_path was me misremembering the API, not a bug in Pyface.

class TestImageEditor(BaseTestMixin, unittest.TestCase):

def test_image_editor_static(self):
obj1 = ImageDisplay()
view = View(
Item(
'image',
editor=ImageEditor(
image=ImageResource(absolute_path=filename1),
),
)
)

# This should not fail.
with create_ui(obj1, dict(view=view)) as ui:
pass

def test_image_editor_resource(self):
obj1 = ImageDisplay(
image=ImageResource(absolute_path=filename1)
)
view = View(
Item(
'image',
editor=ImageEditor()
)
)

# This should not fail.
with create_ui(obj1, dict(view=view)) as ui:
obj1.image = ImageResource(absolute_path=filename2)

def test_image_editor_array(self):
try:
import numpy as np
from pyface.api import ArrayImage
except ImportError:
self.skipTest("NumPy is not available")

gradient1 = np.empty(shape=(256, 256, 3), dtype='uint8')
gradient1[:, :, 0] = np.arange(256).reshape(256, 1)
gradient1[:, :, 1] = np.arange(256).reshape(1, 256)
gradient1[:, :, 2] = np.arange(255, -1, -1).reshape(1, 256)

gradient2 = np.empty(shape=(256, 256, 3), dtype='uint8')
gradient2[:, :, 0] = np.arange(255, -1, -1).reshape(256, 1)
gradient2[:, :, 1] = np.arange(256).reshape(1, 256)
gradient2[:, :, 2] = np.arange(255, -1, -1).reshape(1, 256)

obj1 = ImageDisplay(
image=ArrayImage(data=gradient1)
)
view = View(
Item(
'image',
editor=ImageEditor()
)
)

# This should not fail.
with create_ui(obj1, dict(view=view)) as ui:
obj1.image = ArrayImage(data=gradient2)

def test_image_editor_pillow(self):
try:
import PIL.Image
from pyface.api import PILImage
except ImportError:
self.skipTest("Pillow is not available")

pil_image_1 = PIL.Image.open(filename1)
pil_image_2 = PIL.Image.open(filename2)

obj1 = ImageDisplay(
image=PILImage(image=pil_image_1)
)
view = View(
Item(
'image',
editor=ImageEditor()
)
)

# This should not fail.
with create_ui(obj1, dict(view=view)) as ui:
obj1.image = PILImage(image=pil_image_2)

def test_image_editor_none(self):

obj1 = ImageDisplay()
view = View(
Item(
'image',
editor=ImageEditor()
)
)

# This should not fail.
with create_ui(obj1, dict(view=view)) as ui:
pass
3 changes: 1 addition & 2 deletions traitsui/wx/image_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,4 @@ def update_editor(self):
"""
if self.factory.image is None:
value = self.value
if isinstance(value, ImageResource):
self.control.Bitmap(convert_bitmap(value))
self.control.Bitmap(convert_bitmap(value))