Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Clear() methods to Radio and CheckButtons and other improvements #10924

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

Closed
wants to merge 48 commits into from

Conversation

raamana
Copy link
Contributor

@raamana raamana commented Mar 30, 2018

PR Summary

Closes #10922

I'm unable to set up test environment on my laptop yet, so will use CI tests here.

Not sure if this deserves entries into What's New and API changes.

PR Checklist

  • Code is PEP 8 compliant
  • New features are documented
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@raamana
Copy link
Contributor Author

raamana commented Mar 31, 2018

for some reason, my latest commit to address the PEP test fails is not getting reflected here

@timhoffm
Copy link
Member

timhoffm commented Apr 1, 2018

The commit you've linked is on the master branch, not on radio_checkbox_clear.

@raamana
Copy link
Contributor Author

raamana commented Apr 1, 2018

thanks @timhoffm - I suspected that, but was apparently confused by my IDE's presentation of the VCS log :)


return [l.get_text() for l, box_checked in
zip(self.labels, self.get_status())
if box_checked ]

Choose a reason for hiding this comment

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

if box_checked ] needs to become if box_checked]

That's the last PEP error. You're almost there... 🎉

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, need to configure my IDE better, to not have to go through this so many times :)

@@ -255,11 +255,26 @@ def test_CheckButtons():
assert check.get_status() == [True, False, True]
check.set_active(0)
assert check.get_status() == [False, False, True]
assert check.get_checked() == ['c']
check.clear()
assert not(any(check.get_status()))
Copy link
Member

Choose a reason for hiding this comment

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

assert not any(...)

There is no need for the extra parentheses. not is not a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point - will update it.

def get_status(self):
"""
returns a tuple of the status (True/False) of all of the check buttons
"""
return [l1.get_visible() for (l1, l2) in self.lines]

def get_checked(self):
"""Returns a list of labels currently checked by user."""
Copy link
Member

Choose a reason for hiding this comment

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

Not sure how universally useful this method is. Generally, the labels are a user-facing string aimed at readability and not to identify a state. Only the latter use-case would need this method. This is not something usually found in GUI frameworks, because you tie the state to the displayed string; i.e. when changing the displayed string, you would also have to change your code logic.

I tend not to include this in our public API. However, if other reviewers are ok with it, I'd follow along.

Copy link
Contributor Author

@raamana raamana Apr 2, 2018

Choose a reason for hiding this comment

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

I didn't mean this to be a state indicator - which is clearly and explicitly covered by get_status() with status in its name, but as a result once the user is done checking all the applicable boxes. I am using it in visualqc and there is a clear need for this.

I am not dogmatic about the name, we could improve the name if needed.

Copy link
Member

Choose a reason for hiding this comment

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

Still, you are using the labels as identifiers for the checked elements. This works well for simple cases, and I agree that it's simpler to use than get_status(). However, in general labels don't qualify as identifiers. E.g. among other things labels don't have to be unique.

A more universal solution would associate each check box with a unique identifier and only use the label for the visual text. We don't have this at the moment, but current changes should not make it more difficult to implement.

I would be ok to rename this to get_checked_labels().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting.

Yes, current implementation allows duplicate labels, as python list allows it. But index into the list sufficiently unique for this purpose. Using ‘set’ would make the implementation more complicated.

What do you mean by “you are using the labels as identifiers for the checked elements”?

Copy link
Member

Choose a reason for hiding this comment

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

AFAICS the use case it that you want to know which of the boxes is checked. You want something that describes (identifies) the checked boxes. You are probably thinking of something like

btns = CheckButtons(ax, labels=['a', 'b', 'c'])
...
if 'a' in btns.get_checked():

You use 'a' as an identifier for the first check box, which has label 'a'. This works in the simple example.

However, in general you cannot infer/identify the checked boxes from the associated labels because labels don't have to be unique.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see what you mean now. At a high level, "labels don't have to be unique" is a design decision that has been made already. Using python lists as the underlying data structure is a fine decision too. Discussion on whether to impose uniqueness (representing them with a set perhaps) would be interesting, and is especially well-suited for RadioButtons. Such a decision might require additional logic to maintain valid instantiation of these widgets.

@ImportanceOfBeingErnest
Copy link
Member

It seems that .get_checked_labels() returns the same as np.array(btns.labels)[btns.get_status()] would give. So it's not needed, yet it doesn't hurt, but it makes the code longer.

What's more of a general concern with those methods (this is not really part of this PR but once we're at it...):

  1. The way to get the checked radio button is completely different from the way to get the checked check boxes. With this PR, it would be .index_selected for buttons and .get_status() for the boxes. This is probably rather confusing for people.
  2. The checkboxes' state is queried by using the visibility of the markers. This is pretty dangerous, as turning something invisible should not make it loose its state.

@raamana
Copy link
Contributor Author

raamana commented Apr 2, 2018

It seems that .get_checked_labels() returns the same as np.array(btns.labels)[btns.get_status()] would give. So it's not needed, yet it doesn't hurt, but it makes the code longer.

I think its really helpful for novice users, who would other write a for loop (for lack of expertise in writing that complex expression you put out). Even expert programmers would find it convenient to have it already, so they don't have to write it every time.

The way to get the checked radio button is completely different from the way to get the checked check boxes. With this PR, it would be .index_selected for buttons and .get_status() for the boxes. This is probably rather confusing for people.

I see your point - how about .get_selected() or .index_selected for both?

The checkboxes' state is queried by using the visibility of the markers. This is pretty dangerous, as turning something invisible should not make it loose its state.

I had that same uneasy feeling when I first looked into the implementation. My guess is that the original dev might have made this decsion to ensure what is shown is what is represented inside (WYSIWYG).. Other options are maintaining separate boolean list that the visibility is synced to, not sure if that would be any more reliable.

Getting back to this PR, is it all good to merge? Entries to README or API Changes is not warranted I suppose?

Copy link
Member

@timhoffm timhoffm left a comment

Choose a reason for hiding this comment

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

The widgets could use attention in many aspects (naming, functionality, logic, implementation, docs). But that's beyond this PR.

I've expressed my concern on get_checked_labels(). But given the current code base, this is a helpful function and preferable to np.array(btns.labels)[btns.get_status()] . index_selected is a reasonable name. Also, the set_active() extension and the clear() methods are an improvement. The changes don't complicate future rewrites/improvements.

By me, this is ready to go in. Probably one should sqash the commits.

@raamana
Copy link
Contributor Author

raamana commented Apr 2, 2018

Thanks Tim.

Again, I think set_active should really be renamed to just set, with 2/3 deprecation cycles..

@timhoffm
Copy link
Member

timhoffm commented Apr 2, 2018

Talking about CheckButtons: If we touch the existing API with a deprecation, then we should spit set_active(index, bool) in into set_checked(i, bool) and toggle(i).

Actually, adding set_checked(i, bool) now is probably better than extending set_active(i) in this PR. The latter can then be deprecated/renamed more simply to toggle(i).

@anntzer
Copy link
Contributor

anntzer commented Apr 2, 2018

Out of the scope for that PR, but in case someone wants to rationalize a bit the widgets API (#10924 (review)) I would suggest doing a comparison with the APIs provided by major GUI toolkits (e.g. http://doc.qt.io/qt-5/qbuttongroup.html in this case). We don't have to strictly copy their APIs (which are after all designed for C/C++), but they're probably not completely stupid either.

@jklymak jklymak added this to the v3.2.0 milestone Feb 9, 2019
@jklymak
Copy link
Member

jklymak commented Feb 9, 2019

ping @raamana

@raamana
Copy link
Contributor Author

raamana commented Feb 10, 2019

thanks Jody - I had some trouble setting up the mpl dev environment on my macbook (some issues with gcc, compiling mpl locally etc), so couldn't investigate why tests were failing. Let me try it again now (although Apple seems to be not making it any easier).

@jklymak
Copy link
Member

jklymak commented Feb 10, 2019

You don’t teally need to trigger the tests locally (though it helps). You’d see where ci fails and copy the relevant test to a file and debug as a script.

@raamana
Copy link
Contributor Author

raamana commented Feb 10, 2019

that's a good idea - although I suspect I need to be able to install matplotlib locally in editable/develop mode, before I can run any of the test_* files locally, don't you think? Appreciate your help in seeing this through.

@raamana
Copy link
Contributor Author

raamana commented Feb 10, 2019

On a side note, it would be great if someone made a more "robust" installer script (dev mode) for potential contributors who are good with python but not env management etc.

Anyways I am stuck here:

    running develop
    running egg_info
    writing lib/matplotlib.egg-info/PKG-INFO
    writing dependency_links to lib/matplotlib.egg-info/dependency_links.txt
    writing namespace_packages to lib/matplotlib.egg-info/namespace_packages.txt
    writing requirements to lib/matplotlib.egg-info/requires.txt
    writing top-level names to lib/matplotlib.egg-info/top_level.txt
    reading manifest file 'lib/matplotlib.egg-info/SOURCES.txt'
    reading manifest template 'MANIFEST.in'
    writing manifest file 'lib/matplotlib.egg-info/SOURCES.txt'
    running build_ext
    building 'matplotlib.backends._macosx' extension
    gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -I/Users/Reddy/anaconda3/envs/py36/include -arch x86_64 -I/Users/Reddy/anaconda3/envs/py36/include -arch x86_64 -I/usr/local/include -I/usr/include -I/usr/X11/include -I/opt/X11/include -I. -I/Users/Reddy/anaconda3/envs/py36/include/python3.6m -c src/_macosx.m -o build/temp.macosx-10.7-x86_64-3.6/src/_macosx.o
    gcc: error: src/_macosx.m: Objective-C compiler not installed on this system
    error: command 'gcc' failed with exit status 1
Cleaning up...
Removed build tracker '/private/var/folders/f3/vw4cz6js4zn8fgktmxrfmjsc0000gn/T/pip-req-tracker-_d_xsfmm'
Command "/Users/Reddy/anaconda3/envs/py36/bin/python -c "import setuptools, tokenize;__file__='/Volumes/data/doc/opensource/matplotlib/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" develop --no-deps" failed with error code 1 in /Volumes/data/doc/opensource/matplotlib/
Exception information:
Traceback (most recent call last):
  File "/Users/Reddy/anaconda3/envs/py36/lib/python3.6/site-packages/pip/_internal/cli/base_command.py", line 143, in main
    status = self.run(options, args)
  File "/Users/Reddy/anaconda3/envs/py36/lib/python3.6/site-packages/pip/_internal/commands/install.py", line 366, in run
    use_user_site=options.use_user_site,
  File "/Users/Reddy/anaconda3/envs/py36/lib/python3.6/site-packages/pip/_internal/req/__init__.py", line 49, in install_given_reqs
    **kwargs
  File "/Users/Reddy/anaconda3/envs/py36/lib/python3.6/site-packages/pip/_internal/req/req_install.py", line 750, in install
    install_options, global_options, prefix=prefix,
  File "/Users/Reddy/anaconda3/envs/py36/lib/python3.6/site-packages/pip/_internal/req/req_install.py", line 628, in install_editable
    show_stdout=False,
  File "/Users/Reddy/anaconda3/envs/py36/lib/python3.6/site-packages/pip/_internal/utils/misc.py", line 705, in call_subprocess
    % (command_desc, proc.returncode, cwd))
pip._internal.exceptions.InstallationError: Command "/Users/Reddy/anaconda3/envs/py36/bin/python -c "import setuptools, tokenize;__file__='/Volumes/data/doc/opensource/matplotlib/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" develop --no-deps" failed with error code 1 in /Volumes/data/doc/opensource/matplotlib/

@jklymak
Copy link
Member

jklymak commented Feb 10, 2019

?? What are you doing? Maybe take to the Gitter channel...

@QuLogic
Copy link
Member

QuLogic commented Feb 10, 2019

gcc: error: src/_macosx.m: Objective-C compiler not installed on this system

You need Xcode, most likely.

@raamana
Copy link
Contributor Author

raamana commented Feb 10, 2019

@jklymak, I am trying to install matplotlib in developer mode with python -mpip install -ve .

@QuLogic, I have the latest Xcode, and installed all the components it needs.

@timhoffm timhoffm modified the milestones: v3.2.0, v3.3.0 Aug 16, 2019
@QuLogic
Copy link
Member

QuLogic commented Apr 1, 2020

This is replaced by #13401.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ENH: clear() method for widgets.RadioButtons
7 participants