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

Toolkit independent color class for Pyface #608

Merged
merged 3 commits into from
Jul 21, 2020
Merged

Conversation

corranwebster
Copy link
Contributor

This is the color class from #534 pulled out into a separate PR.

This does not include any of the string parsing, trait type, or dialog classes.

@codecov-commenter
Copy link

codecov-commenter commented Jul 18, 2020

Codecov Report

Merging #608 into master will increase coverage by 0.23%.
The diff coverage is 94.91%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #608      +/-   ##
==========================================
+ Coverage   38.97%   39.21%   +0.23%     
==========================================
  Files         487      491       +4     
  Lines       26798    26916     +118     
  Branches     4066     4069       +3     
==========================================
+ Hits        10445    10555     +110     
- Misses      15897    15904       +7     
- Partials      456      457       +1     
Impacted Files Coverage Δ
pyface/ui/null/color.py 0.00% <0.00%> (ø)
pyface/color.py 100.00% <100.00%> (ø)
pyface/ui/qt4/color.py 100.00% <100.00%> (ø)
pyface/ui/wx/color.py 100.00% <100.00%> (ø)
pyface/ui/qt4/code_editor/code_widget.py 43.73% <0.00%> (-0.43%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0f3202b...d5d331a. Read the comment docs.

Copy link
Contributor

@rahulporuri rahulporuri left a comment

Choose a reason for hiding this comment

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

Mostly LGTM with a few nit-picky comments and questions.

pyface/ui/null/color.py Outdated Show resolved Hide resolved
pyface/color.py Outdated Show resolved Hide resolved
pyface/color.py Show resolved Hide resolved
pyface/color.py Show resolved Hide resolved
pyface/tests/test_color.py Outdated Show resolved Hide resolved
pyface/tests/test_color.py Outdated Show resolved Hide resolved
pyface/tests/test_color.py Outdated Show resolved Hide resolved
pyface/tests/test_color.py Show resolved Hide resolved
@rahulporuri
Copy link
Contributor

FTR, I went through the older/larger PR - #534 - and there aren't any comments on that PR which are relevant here that hadn't been addressed.

@rahulporuri
Copy link
Contributor

PR still LGTM

@rahulporuri
Copy link
Contributor

@kitchoi can you make time to review this PR over the next couple of days?

@kitchoi
Copy link
Contributor

kitchoi commented Jul 20, 2020

can you make time to review this PR over the next couple of days?

Yep can do.

Copy link
Contributor

@kitchoi kitchoi left a comment

Choose a reason for hiding this comment

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

LGTM, some questions and minor suggestions.

Comment on lines +94 to +95
Colors implement equality testing, but are not hashable as they are
mutable, and so are not suitable for use as dictionary keys. If you
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we actually need Color to be mutable? e.g. in all our use cases, could changing colors be done by supplying a new instance of Color instead of mutating its traits?

We can make Color immutable by making rgba read-only and required at instantiation. This can be achieved by using ReadOnly trait type, or setting the ctrait_type to readonly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The objects that this is intended to replace (QColor and wx.Colour values of the Color trait) are mutable.

If we were to make this immutable, the design would be a lot different (probably a namedtuple rather than a HasTraits)

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it depends on how this object is to be used. If we only ever interface it with the GUI toolkit using from_toolkit and to_toolkit, then we might not need to mutate the traits on this object at all.

Immutables are much easier to work with (and are much easier to maintain), especially in asynchronous context (GUI is such context). It is probably also easier to start with an immutable, and then turn it into a mutable later. And if later we run into a situation where this object really needs to be mutated, we have a reason to do it. On the other hand, it would be harder to make a mutable an immutable later.

I will leave that up to you whether you want to consider turning this an immutable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I expect that the to/from toolkit functions will be used at the point of need internally by toolkit-specific code and not exposed to typical users. One goal of this exercise is that eventually we should never have someone have to use a QColour or wx.Colour unless they are writing toolkit-specific code.

return NotImplemented

def __str__(self):
return "({:0.5}, {:0.5}, {:0.5}, {:0.5})".format(*self.rgba)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this __str__ used for?

This reminds me of Python 2 behaviour where str(float) does not roundstrip, and it has caused issues because it was not at all obvious (e.g. marshmallow Float uses str, and it causes issue for people who try to use marshmallow for serializing data in JSON and deserialized back, and expecting the data to stay the same... and then be surprised when they don't).

That Python 2 has since changed in Python 3 such that str(float) does round trip. I am wondering whatever that had bitten people before with str(float) not roundtripping might bite us here.

To be safe, could we use {!r} here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point of __str__ is always a human-friendly representation of an object, suitable for use in a print statement or, in the case of TraitsUI, the default representation in a UI. Round-tripping is always a secondary consideration.

In particular, the motivation for this is what will appear in a TraitsUI colour editor's text field.

Given that in practice the colours are going to be converted to bytes, they key thing is that the round-trip byte -> float -> str -> float -> byte works (and it does) so that the actual color doesn't change.

Actually, we can even get away with {0:3} here.

If there is a specialist case which requires more precision, the user can override the editor's format function to give full precision.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah - I remember why 5. The CSS styles allow you to specify with 4-digit hex values if you want. 5 places allows round-tripping all of those successfully.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am wondering if this is actually a view layer that gets put on the model. Perhaps this conversion could be implemented in the view instead of here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It could, but you want a default, and since this default also makes sense for other situations where you want a human-friendly representation, I think it makes sense to have a __str__ method.

Otherwise why does Python have a __str__ at all?

Copy link
Contributor

Choose a reason for hiding this comment

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

For a library, it allows more room for modifications later if we are more conservative about the functionality exposed. The tools are there but we may not choose to use it, e.g. namedtuple is there but we would be exposing more capability than we need if we use namedtuple for something where we only needed named attribute access, making it hard to back out later. Anyway, it was just a thought.


from unittest import TestCase

from traits.testing.unittest_tools import UnittestTools
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: It is available from traits.testing.api, it would be preferred to import from api.

)


def channels_to_ints(channels, maximum=255):
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we never use a different value for maximum. When do we need to set a different value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do in the parsing code in the other PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK

@corranwebster
Copy link
Contributor Author

I think everything is addressed. Will merge in a little while.

@corranwebster corranwebster merged commit 2c85065 into master Jul 21, 2020
@corranwebster corranwebster deleted the feat/color-class branch July 21, 2020 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants