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

DataView checkboxes #694

Merged
merged 5 commits into from
Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 11 additions & 2 deletions examples/data_view/column_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
from functools import partial
from random import choice, randint

from traits.api import Dict, HasStrictTraits, Instance, Int, Str, List
from traits.api import Bool, Dict, HasStrictTraits, Instance, Int, Str, List

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
from pyface.data_view.value_types.api import (
BoolValue, IntValue, TextValue, no_value
)

from column_data_model import (
AbstractRowInfo, ColumnDataModel, HasTraitsRowInfo
Expand All @@ -46,6 +48,8 @@ class Person(HasStrictTraits):

age = Int

contacted = Bool

address = Instance(Address)


Expand All @@ -72,6 +76,11 @@ def get_image(self, model, row, column):
value="age",
value_type=IntValue(minimum=0),
),
HasTraitsRowInfo(
title="Contacted",
value="contacted",
value_type=BoolValue(true_text="Yes", false_text="No"),
),
HasTraitsRowInfo(
title="Address",
value_type=no_value,
Expand Down
79 changes: 79 additions & 0 deletions pyface/data_view/abstract_value_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@
and how to actually display it.
"""

from enum import IntEnum

from traits.api import ABCHasStrictTraits, Event, observe

from .data_view_errors import DataViewSetError


class CheckState(IntEnum):
"Possible checkbox states"
# XXX in the future this may need a "partial" state, see Pyface #695
UNCHECKED = 0
CHECKED = 1


class AbstractValueType(ABCHasStrictTraits):
""" A value type converts raw data into data channels.

Expand Down Expand Up @@ -226,6 +235,76 @@ def get_image(self, model, row, column):
from pyface.image_resource import ImageResource
return ImageResource("image_not_found")

def has_check_state(self, model, row, column):
""" Whether or not the value has checked state.

The default implementation returns False.

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_check_state : bool
Whether or not the value has a checked state.
"""
return False

def get_check_state(self, model, row, column):
""" The state of the item check box.

The default implementation returns "checked" if the value is
truthy, or "unchecked" if the value is falsey.

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
-------
check_state : CheckState
The current checked state.
"""
return (
CheckState.CHECKED
if model.get_value(row, column)
else CheckState.UNCHECKED
)
Comment on lines +279 to +283
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why the default get_check_state doesn't return a DataViewGetError like set_check_state does.

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 is a default implementation that is almost always what you want: if the value has a check state, then it should be checked if the underlying value is truthy and unchecked if it is falsey.

By implementing it on the ABC we save repetition elsewhere (we do similar things for get text and get image, try to have a sane default for the getter).


def set_check_state(self, model, row, column, check_state):
""" Set the checked state of the underlying value.

The default implementation does not allow setting the checked state.

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.
check_state : CheckState
The check state value to set.

Raises
-------
DataViewSetError
If the value cannot be set.
"""
raise DataViewSetError("Cannot set check state.")

def has_tooltip(self, model, row, column):
""" Whether or not the value has a tooltip.

Expand Down
17 changes: 16 additions & 1 deletion pyface/data_view/tests/test_abstract_value_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from traits.testing.unittest_tools import UnittestTools

from pyface.data_view.data_view_errors import DataViewSetError
from pyface.data_view.abstract_value_type import AbstractValueType
from pyface.data_view.abstract_value_type import AbstractValueType, CheckState


class ValueType(AbstractValueType):
Expand Down Expand Up @@ -85,6 +85,21 @@ def test_get_image(self):
result = value_type.get_image(self.model, [0], [0])
self.assertEqual(result.name, "image_not_found")

def test_has_check_state(self):
value_type = ValueType()
result = value_type.has_check_state(self.model, [0], [0])
self.assertFalse(result)

def test_get_check_state(self):
value_type = ValueType()
result = value_type.get_check_state(self.model, [0], [0])
self.assertEqual(result, CheckState.CHECKED)
rahulporuri marked this conversation as resolved.
Show resolved Hide resolved

def test_set_check_state(self):
value_type = ValueType()
with self.assertRaises(DataViewSetError):
value_type.set_check_state(self.model, [0], [0], CheckState.CHECKED)

def test_parameter_update(self):
value_type = ValueType()
with self.assertTraitChanges(value_type, 'updated', count=1):
Expand Down
1 change: 1 addition & 0 deletions pyface/data_view/value_types/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#
# Thanks for using Enthought open source!

from .bool_value import BoolValue # noqa: F401
from .constant_value import ConstantValue # noqa: F401
from .editable_value import EditableValue # noqa: F401
from .no_value import NoValue, no_value # noqa: F401
Expand Down
108 changes: 108 additions & 0 deletions pyface/data_view/value_types/bool_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# (C) Copyright 2005-2020 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!

from traits.api import Str

from pyface.data_view.abstract_value_type import AbstractValueType, CheckState


class BoolValue(AbstractValueType):
""" Value that presents a boolean value via checked state.
"""

#: The text to display next to a True value.
true_text = Str()

#: The text to display next to a False value.
false_text = Str()

def has_editor_value(self, model, row, column):
""" BoolValues don't use editors, but have always-on checkbox.

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_editor_value : bool
Whether or not the value is editable.
"""
return False

def get_text(self, model, row, column):
""" The textual representation of the underlying 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
-------
text : str
The textual representation of the underlying value.
"""
return (
self.true_text if model.get_value(row, column) else self.false_text
)

def has_check_state(self, model, row, column):
""" Whether or not the value has checked state.

The default implementation returns True.

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_check_state : bool
Whether or not the value has a checked state.
"""
return True

def set_check_state(self, model, row, column, check_state):
""" Set the boolean value from the check state.

Parameters
----------
model : AbstractDataModel
The data model holding the data.
row : sequence of int
The row in the data model being set.
column : sequence of int
The column in the data model being set.
check_state : "checked" or "unchecked"
The check state being set.

Raises
-------
DataViewSetError
If the value cannot be set.
"""
value = (check_state == CheckState.CHECKED)
model.set_value(row, column, value)
85 changes: 85 additions & 0 deletions pyface/data_view/value_types/tests/test_bool_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# (C) Copyright 2005-2020 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!

from unittest import TestCase
from unittest.mock import Mock

from pyface.data_view.abstract_value_type import CheckState
from pyface.data_view.data_view_errors import DataViewSetError
from pyface.data_view.value_types.bool_value import BoolValue


class TestBoolValue(TestCase):

def setUp(self):
self.model = Mock()
self.model.get_value = Mock(return_value=True)
self.model.can_set_value = Mock(return_value=True)
self.model.set_value = Mock()

def test_defaults(self):
value = BoolValue()
self.assertEqual(value.true_text, "")
self.assertEqual(value.false_text, "")

def test_has_text_default(self):
value = BoolValue()
has_text = value.has_text(self.model, [0], [0])
self.assertFalse(has_text)

def test_has_text(self):
value = BoolValue(true_text="Yes", false_text="No")
has_text = value.has_text(self.model, [0], [0])
self.assertTrue(has_text)

def test_get_text_default(self):
value = BoolValue()
text = value.get_text(self.model, [0], [0])
self.assertEqual(text, "")

self.model.get_value = Mock(return_value=False)
text = value.get_text(self.model, [0], [0])
self.assertEqual(text, "")

def test_get_text(self):
value = BoolValue(true_text="Yes", false_text="No")
text = value.get_text(self.model, [0], [0])
self.assertEqual(text, "Yes")

self.model.get_value = Mock(return_value=False)
text = value.get_text(self.model, [0], [0])
self.assertEqual(text, "No")

def test_get_check_state(self):
value = BoolValue()
check_state = value.get_check_state(self.model, [0], [0])
self.assertEqual(check_state, CheckState.CHECKED)

def test_get_check_state_false(self):
value = BoolValue()
self.model.get_value = Mock(return_value=False)
check_state = value.get_check_state(self.model, [0], [0])
self.assertEqual(check_state, CheckState.UNCHECKED)

def test_set_check_state(self):
value = BoolValue()
value.set_check_state(self.model, [0], [0], CheckState.CHECKED)
self.model.set_value.assert_called_once_with([0], [0], True)

def test_set_check_state_unchecked(self):
value = BoolValue()
value.set_check_state(self.model, [0], [0], CheckState.UNCHECKED)
self.model.set_value.assert_called_once_with([0], [0], False)

def test_set_check_state_no_set_value(self):
self.model.can_set_value = Mock(return_value=False)
value = BoolValue()
with self.assertRaises(DataViewSetError):
value.set_text(self.model, [0], [0], CheckState.CHECKED)
Loading