From 093f9cc310af96c339d43bfe22bf66fe6b9c2235 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Aug 2020 11:29:35 +0100 Subject: [PATCH 1/6] Add support for images to value types. --- pyface/data_view/abstract_value_type.py | 44 +++++++++++++++++++ .../data_view/value_types/constant_value.py | 11 ++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/pyface/data_view/abstract_value_type.py b/pyface/data_view/abstract_value_type.py index 59d600732..75cfae783 100644 --- a/pyface/data_view/abstract_value_type.py +++ b/pyface/data_view/abstract_value_type.py @@ -182,6 +182,50 @@ def set_text(self, model, row, column, text): """ raise DataViewSetError("Cannot set value.") + def has_image(self, model, row, column): + """ Whether or not the value has an image associated with it. + + The default implementation returns True if ``get_image`` + returns a non-None value. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + has_image : bool + Whether or not the value has an image associated with it. + """ + return self.get_image(model, row, column) is not None + + def get_image(self, model, row, column): + """ An image associated with the underlying value. + + The default implementation returns None. + + Parameters + ---------- + model : AbstractDataModel + The data model holding the data. + row : sequence of int + The row in the data model being queried. + column : sequence of int + The column in the data model being queried. + + Returns + ------- + text : toolkit Image or None + The image associated with the underlying value, or None if there + is no image. + """ + return None + @observe('+update_value_type') def update_value_type(self, event=None): """ Fire update event when marked traits change. """ diff --git a/pyface/data_view/value_types/constant_value.py b/pyface/data_view/value_types/constant_value.py index 7b6146a16..d185e6380 100644 --- a/pyface/data_view/value_types/constant_value.py +++ b/pyface/data_view/value_types/constant_value.py @@ -8,9 +8,10 @@ # # Thanks for using Enthought open source! -from traits.api import Str +from traits.api import Instance, Str from pyface.data_view.abstract_value_type import AbstractValueType +from pyface.i_image_resource import IImageResource class ConstantValue(AbstractValueType): @@ -24,8 +25,16 @@ class ConstantValue(AbstractValueType): #: The text value to display. text = Str(update_value_type=True) + #: The image value to display. + image = Instance(IImageResource, update_value_type=True) + def has_editor_value(self, model, row, column): return False def get_text(self, model, row, column): return self.text + + def get_image(self, model, row, column): + if isinstance(self.image, IImageResource): + return self.image.create_image() + return None From f1d915ea94b183184a19557903f6f374d1afc258 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Aug 2020 11:30:03 +0100 Subject: [PATCH 2/6] Add image support to Qt dataview. --- pyface/ui/qt4/data_view/data_view_item_model.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyface/ui/qt4/data_view/data_view_item_model.py b/pyface/ui/qt4/data_view/data_view_item_model.py index 67839276d..e6891e05d 100644 --- a/pyface/ui/qt4/data_view/data_view_item_model.py +++ b/pyface/ui/qt4/data_view/data_view_item_model.py @@ -148,6 +148,9 @@ def data(self, index, role=Qt.DisplayRole): elif role == Qt.EditRole: if value_type.has_editor_value(self.model, row, column): return value_type.get_editor_value(self.model, row, column) + elif role == Qt.DecorationRole: + if value_type.has_image(self.model, row, column): + return value_type.get_image(self.model, row, column) return None From 9dfc93b051073699fca1d3f9aaabdffe9c3412fc Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Aug 2020 11:30:26 +0100 Subject: [PATCH 3/6] Add images to column example. --- examples/data_view/column_example.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/data_view/column_example.py b/examples/data_view/column_example.py index ba244db73..581939720 100644 --- a/examples/data_view/column_example.py +++ b/examples/data_view/column_example.py @@ -14,9 +14,9 @@ from functools import partial from random import choice, randint -from traits.api import HasStrictTraits, Instance, Int, Str, List +from traits.api import Dict, HasStrictTraits, Instance, Int, Str, List -from pyface.api import ApplicationWindow, GUI +from pyface.api import ApplicationWindow, GUI, Image, ImageResource from pyface.data_view.i_data_view_widget import IDataViewWidget from pyface.data_view.data_view_widget import DataViewWidget from pyface.data_view.value_types.api import IntValue, TextValue, no_value @@ -26,6 +26,9 @@ ) +uk_flag = ImageResource('gb.png') +us_flag = ImageResource('us.png') + class Address(HasStrictTraits): street = Str @@ -44,6 +47,17 @@ class Person(HasStrictTraits): address = Instance(Address) +class CountryValue(TextValue): + + flags = Dict(Str, Image, update_value_type=True) + + def get_image(self, model, row, column): + value = model.get_value(row, column) + if value in self.flags: + return self.flags[value].create_image() + return None + + row_info = HasTraitsRowInfo( title='People', value='name', @@ -72,7 +86,12 @@ class Person(HasStrictTraits): HasTraitsRowInfo( title="Country", value="address.country", - value_type=TextValue(), + value_type=CountryValue( + flags={ + 'UK': uk_flag, + 'USA': us_flag, + } + ), ), ], ), From 20edfb9c858c24aadef1a085fcf9c02217066626 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Aug 2020 17:18:00 +0100 Subject: [PATCH 4/6] Update to give IImageResource to dataview, rather than toolkit object. --- examples/data_view/column_example.py | 13 +++++----- pyface/data_view/abstract_value_type.py | 2 +- .../data_view/value_types/constant_value.py | 7 +++--- pyface/data_view/value_types/no_value.py | 3 +++ .../value_types/tests/test_constant_value.py | 24 +++++++++++++++++++ .../value_types/tests/test_no_value.py | 4 ++++ .../ui/qt4/data_view/data_view_item_model.py | 5 +++- 7 files changed, 46 insertions(+), 12 deletions(-) diff --git a/examples/data_view/column_example.py b/examples/data_view/column_example.py index 581939720..ba6342c01 100644 --- a/examples/data_view/column_example.py +++ b/examples/data_view/column_example.py @@ -25,9 +25,11 @@ AbstractRowInfo, ColumnDataModel, HasTraitsRowInfo ) +flags = { + 'UK': ImageResource('gb.png'), + 'USA': ImageResource('us.png'), +} -uk_flag = ImageResource('gb.png') -us_flag = ImageResource('us.png') class Address(HasStrictTraits): @@ -54,7 +56,7 @@ class CountryValue(TextValue): def get_image(self, model, row, column): value = model.get_value(row, column) if value in self.flags: - return self.flags[value].create_image() + return self.flags[value] return None @@ -87,10 +89,7 @@ def get_image(self, model, row, column): title="Country", value="address.country", value_type=CountryValue( - flags={ - 'UK': uk_flag, - 'USA': us_flag, - } + flags=flags, ), ), ], diff --git a/pyface/data_view/abstract_value_type.py b/pyface/data_view/abstract_value_type.py index 75cfae783..7a0478b46 100644 --- a/pyface/data_view/abstract_value_type.py +++ b/pyface/data_view/abstract_value_type.py @@ -220,7 +220,7 @@ def get_image(self, model, row, column): Returns ------- - text : toolkit Image or None + image : IImageResource or None The image associated with the underlying value, or None if there is no image. """ diff --git a/pyface/data_view/value_types/constant_value.py b/pyface/data_view/value_types/constant_value.py index d185e6380..c3e0c82d6 100644 --- a/pyface/data_view/value_types/constant_value.py +++ b/pyface/data_view/value_types/constant_value.py @@ -8,10 +8,11 @@ # # Thanks for using Enthought open source! -from traits.api import Instance, Str +from traits.api import Str from pyface.data_view.abstract_value_type import AbstractValueType from pyface.i_image_resource import IImageResource +from pyface.ui_traits import Image class ConstantValue(AbstractValueType): @@ -26,7 +27,7 @@ class ConstantValue(AbstractValueType): text = Str(update_value_type=True) #: The image value to display. - image = Instance(IImageResource, update_value_type=True) + image = Image(update_value_type=True) def has_editor_value(self, model, row, column): return False @@ -36,5 +37,5 @@ def get_text(self, model, row, column): def get_image(self, model, row, column): if isinstance(self.image, IImageResource): - return self.image.create_image() + return self.image return None diff --git a/pyface/data_view/value_types/no_value.py b/pyface/data_view/value_types/no_value.py index 085a9be8a..c6cebd69c 100644 --- a/pyface/data_view/value_types/no_value.py +++ b/pyface/data_view/value_types/no_value.py @@ -20,6 +20,9 @@ def has_editor_value(self, model, row, column): def has_text(self, model, row, column): return False + def has_image(self, model, row, column): + return False + #: Standard instance of the NoValue class, since it has no state. no_value = NoValue() diff --git a/pyface/data_view/value_types/tests/test_constant_value.py b/pyface/data_view/value_types/tests/test_constant_value.py index 025dc1298..358dab421 100644 --- a/pyface/data_view/value_types/tests/test_constant_value.py +++ b/pyface/data_view/value_types/tests/test_constant_value.py @@ -14,6 +14,7 @@ from traits.testing.unittest_tools import UnittestTools from pyface.data_view.value_types.constant_value import ConstantValue +from pyface.image_resource import ImageResource class TestConstantValue(UnittestTools, TestCase): @@ -49,3 +50,26 @@ def test_text_changed(self): with self.assertTraitChanges(value_type, 'updated'): value_type.text = 'something' self.assertEqual(value_type.text, 'something') + + def test_has_image(self): + value_type = ConstantValue() + self.assertFalse(value_type.has_image(self.model, [0], [0])) + + def test_has_image_true(self): + value_type = ConstantValue(image="question") + self.assertTrue(value_type.has_image(self.model, [0], [0])) + + def test_get_image(self): + image = ImageResource("question") + value_type = ConstantValue(image=image) + self.assertEqual( + value_type.get_image(self.model, [0], [0]), + image + ) + + def test_image_changed(self): + value_type = ConstantValue() + image = ImageResource("question") + with self.assertTraitChanges(value_type, 'updated'): + value_type.image = image + self.assertEqual(value_type.image, image) diff --git a/pyface/data_view/value_types/tests/test_no_value.py b/pyface/data_view/value_types/tests/test_no_value.py index 1e4a7659e..1065871b7 100644 --- a/pyface/data_view/value_types/tests/test_no_value.py +++ b/pyface/data_view/value_types/tests/test_no_value.py @@ -26,3 +26,7 @@ def test_has_editor_value(self): def test_has_text(self): value_type = NoValue() self.assertFalse(value_type.has_text(self.model, [0], [0])) + + def test_has_image(self): + value_type = NoValue() + self.assertFalse(value_type.has_image(self.model, [0], [0])) diff --git a/pyface/ui/qt4/data_view/data_view_item_model.py b/pyface/ui/qt4/data_view/data_view_item_model.py index e6891e05d..00b8386f9 100644 --- a/pyface/ui/qt4/data_view/data_view_item_model.py +++ b/pyface/ui/qt4/data_view/data_view_item_model.py @@ -10,6 +10,7 @@ import logging +from pyface.i_image_resource import IImageResource from pyface.qt import is_qt5 from pyface.qt.QtCore import QAbstractItemModel, QModelIndex, Qt from pyface.data_view.index_manager import Root @@ -150,7 +151,9 @@ def data(self, index, role=Qt.DisplayRole): return value_type.get_editor_value(self.model, row, column) elif role == Qt.DecorationRole: if value_type.has_image(self.model, row, column): - return value_type.get_image(self.model, row, column) + image = value_type.get_image(self.model, row, column) + if isinstance(image, IImageResource): + return image.create_image() return None From d57301a953e8257abea7c5e960c669a7807598eb Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Fri, 21 Aug 2020 17:21:39 +0100 Subject: [PATCH 5/6] Add images for example, plus license file. --- examples/data_view/images/gb.png | Bin 0 -> 599 bytes examples/data_view/images/image_LICENSE.txt | 15 +++++++++++++++ examples/data_view/images/us.png | Bin 0 -> 609 bytes 3 files changed, 15 insertions(+) create mode 100644 examples/data_view/images/gb.png create mode 100644 examples/data_view/images/image_LICENSE.txt create mode 100755 examples/data_view/images/us.png diff --git a/examples/data_view/images/gb.png b/examples/data_view/images/gb.png new file mode 100644 index 0000000000000000000000000000000000000000..ff701e19f6d2c0658fb23b1d94124cba4ce60851 GIT binary patch literal 599 zcmV-d0;v6oP)U(k2*|8J(R-+sudaynhucHbwAMTnor{mwqO^w7JHzaBsT z{O^B8RYf5+LvDs&KmRKVd78=o{`1#HTiEo_OolaGleS)G+IQ#sUI`b*pv<`1zCJ=H0jd{{2S>p`ri%{LsXJ%FbMS z$#S`6f|?OG!^Jxczkf6Q`UNF{l0Sd`ad7zm>({^EzyAS6{{CgrkluOb3l1A>ZU2~A zK+FZ=zkmP!`TOVhpFbzBzFaPmD2$N3;+$pK?>zdet`f0002ovPDHLkV1gy;I?Vt8 literal 0 HcmV?d00001 diff --git a/examples/data_view/images/image_LICENSE.txt b/examples/data_view/images/image_LICENSE.txt new file mode 100644 index 000000000..a4246bfd0 --- /dev/null +++ b/examples/data_view/images/image_LICENSE.txt @@ -0,0 +1,15 @@ +The icons are mostly derived work from other icons. As such they are +licensed accordingly to the original license: + +Project License File +------------------ --------------- ----------------------------------------- +FamFamFam Flags Public Domain http://www.famfamfam.com/lab/icons/flags/ + +Unless stated in this file, icons are the work of Enthought, and are +released under a 3 clause BSD license. + +Files and orginal authors: +---------------------------------------------------------------------------- +examples/data_view/images: + gb.png | FamFamFam Flags + us.png | FamFamFam Flags diff --git a/examples/data_view/images/us.png b/examples/data_view/images/us.png new file mode 100755 index 0000000000000000000000000000000000000000..10f451fe85c41c6c9a06d543a57114ae2f87ecc1 GIT binary patch literal 609 zcmV-n0-pVeP){qW|y?pud`Sa)3|NY&vWd%S0u>b>P!2!lUe;6EF*#G_c zFVXVt@6Q{uX@40W{p0iY2Aa+A^Cu7i8KT+YH}2j52q4BskM2rJ$^k9;2Xxc_|Np=M z&VaLlA*IO5FlECMfB<5VUNC{tBZO(|zW*;@GJN;|bTJ71`0*d;`d`2P!x=ymOA`2> z+y@9C##^^8%gd{MW@Y91_2d742B2~OQNf=-zkmD?Vqkdk_wPTUNeuu2#KPTG{_;O4 v7C%8E5*DLB7#Kb?Fnj}}-(W6879hX?8lYRg`Y`<~00000NkvXXu0mjfD6Jtx literal 0 HcmV?d00001 From 44f134bde9469a8e1df98a60998032041013b951 Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Tue, 25 Aug 2020 11:06:36 +0100 Subject: [PATCH 6/6] Prohibit None image values. --- pyface/data_view/abstract_value_type.py | 10 +++++----- pyface/data_view/tests/test_abstract_value_type.py | 10 ++++++++++ pyface/data_view/value_types/constant_value.py | 8 +++++--- .../data_view/value_types/tests/test_constant_value.py | 5 +++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/pyface/data_view/abstract_value_type.py b/pyface/data_view/abstract_value_type.py index 7a0478b46..09d29af4d 100644 --- a/pyface/data_view/abstract_value_type.py +++ b/pyface/data_view/abstract_value_type.py @@ -202,7 +202,7 @@ def has_image(self, model, row, column): has_image : bool Whether or not the value has an image associated with it. """ - return self.get_image(model, row, column) is not None + return False def get_image(self, model, row, column): """ An image associated with the underlying value. @@ -220,11 +220,11 @@ def get_image(self, model, row, column): Returns ------- - image : IImageResource or None - The image associated with the underlying value, or None if there - is no image. + image : IImageResource + The image associated with the underlying value. """ - return None + from pyface.image_resource import ImageResource + return ImageResource("image_not_found") @observe('+update_value_type') def update_value_type(self, event=None): diff --git a/pyface/data_view/tests/test_abstract_value_type.py b/pyface/data_view/tests/test_abstract_value_type.py index d4297846b..f026c7ee9 100644 --- a/pyface/data_view/tests/test_abstract_value_type.py +++ b/pyface/data_view/tests/test_abstract_value_type.py @@ -74,6 +74,16 @@ def test_set_text(self): with self.assertRaises(DataViewSetError): value_type.set_text(self.model, [0], [0], "2.0") + def test_has_image(self): + value_type = ValueType() + result = value_type.has_image(self.model, [0], [0]) + self.assertFalse(result) + + def test_get_image(self): + value_type = ValueType() + result = value_type.get_image(self.model, [0], [0]) + self.assertEqual(result.name, "image_not_found") + def test_parameter_update(self): value_type = ValueType() with self.assertTraitChanges(value_type, 'updated', count=1): diff --git a/pyface/data_view/value_types/constant_value.py b/pyface/data_view/value_types/constant_value.py index c3e0c82d6..2a6a26c9b 100644 --- a/pyface/data_view/value_types/constant_value.py +++ b/pyface/data_view/value_types/constant_value.py @@ -11,7 +11,6 @@ from traits.api import Str from pyface.data_view.abstract_value_type import AbstractValueType -from pyface.i_image_resource import IImageResource from pyface.ui_traits import Image @@ -35,7 +34,10 @@ def has_editor_value(self, model, row, column): def get_text(self, model, row, column): return self.text + def has_image(self, model, row, column): + return self.image is not None + def get_image(self, model, row, column): - if isinstance(self.image, IImageResource): + if self.image is not None: return self.image - return None + return super().get_image(model, row, column) diff --git a/pyface/data_view/value_types/tests/test_constant_value.py b/pyface/data_view/value_types/tests/test_constant_value.py index 358dab421..259c201a5 100644 --- a/pyface/data_view/value_types/tests/test_constant_value.py +++ b/pyface/data_view/value_types/tests/test_constant_value.py @@ -67,6 +67,11 @@ def test_get_image(self): image ) + def test_get_image_none(self): + value_type = ConstantValue() + image = value_type.get_image(self.model, [0], [0]) + self.assertEqual(image.name, "image_not_found") + def test_image_changed(self): value_type = ConstantValue() image = ImageResource("question")