Skip to content

Implemented :meth:.Mobject.set_default, a mechanism for changing default values of keyword arguments #2075

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

Merged
merged 14 commits into from
Sep 30, 2021

Conversation

behackl
Copy link
Member

@behackl behackl commented Sep 24, 2021

Overview: What does this pull request change?

There has been a lot of discussion about default values for Mobjects (I'll go through the list of issues and link the related ones in a bit). This is a very, very, very, simple (read: it is not particularly clever or involved) proposal which allows users to change the default values of the keyword arguments of a given class.

This new feature allows to change the default value of keyword arguments passed to the constructor of mobjects.
For example, after running Text.set_default(color=BLACK), new texts like Text("Hello world!")
will be colored black by default. See the documentation of :meth:.Mobject.set_default for more
details.

Motivation and Explanation: Why and how do your changes improve the library?

Changing default values is something very common, and it should be easy to do. Speaking from a big-picture point of view, there are two possible approaches: storing default values in a separate module (config.defaults?), or storing them in the mobject itself.

I like this proposed solution, because (a) it is rather simple (b) if we decide to add more infrastructure to default values (e.g., keeping track of values, allowing to save/restore configurations, whatever) the method-interface via .change_default(**kwargs) can be extended and appropriately modified easily without introducing breaking changes.

Links to added or changed documentation pages

https://manimce--2075.org.readthedocs.build/en/2075/reference/manim.mobject.mobject.Mobject.html#manim.mobject.mobject.Mobject.change_default

Further Information and Comments

Reviewer Checklist

  • The PR title is descriptive enough for the changelog, and the PR is labeled correctly
  • If applicable: newly added non-private functions and classes have a docstring including a short summary and a PARAMETERS section
  • If applicable: newly added functions and classes are tested

@behackl behackl added the new feature Enhancement specifically adding a new feature (feature request should be used for issues instead) label Sep 24, 2021
@behackl behackl marked this pull request as ready for review September 24, 2021 18:41
@kolibril13
Copy link
Member

I really like this approach, especially in Jupyter notebooks it has the potential to simplify the workflow, I tested it with this notebook:
image
One idea: what about changing the name to mob.default(color=BLACK) instead of mob.change_default(color=BLACK)?

@kolibril13
Copy link
Member

... I just discovered one possible error: When one tries to set a non-existing parameter, or misspells a parameter, setting the parameter is still possible. Only when the mobject is called later, the error occurs (see screenshot below).
As there is nothing like auto-completion in the change_default method, it's quite easy to get a typo in these parameters.
In Jupyter notebooks, as soon as there is typo, the Mobject is broken in all cells.
It is still broken, when another from manim import * is called. Only restarting the kernel will help, but when you are in the middle of creating a scene, restarting the kernel might be inconvenient.
My idea: A) throwing an error when one tries to set a parameter that is not existing.
B) an easy option to "Reset" the mobject to the default parameters.
C) enabling auto-completion for this change default parameter.

image

@kolibril13
Copy link
Member

and one further comment:
When I would change the color of MathTex, then also the color of Matrix would get changed, as it initializes MathTex.
I think this is more useful than annoying, but still might sometimes lead to unexpected situations:
image

@behackl
Copy link
Member Author

behackl commented Sep 25, 2021

Thanks for checking this out and for your comments, @kolibril13!

One idea: what about changing the name to mob.default(color=BLACK) instead of mob.change_default(color=BLACK)?

What would be the benefit? I think change_default is more explicit and readable.

I just discovered one possible error: When one tries to set a non-existing parameter, or misspells a parameter, setting the parameter is still possible

I don't see an effective way of dealing with this, unfortunately -- a class like MathTex does not know which keyword arguments can be passed to it, because it only processes a few keywords (which we could identify), but we don't know which keywords the constructors of the parent classes accept. (To be more concrete: we could check for keywords explicitly accepted by, e.g., MathTex -- but that would result in MathTex.change_default(color=...) raising an error, because color is not processed by MathTex, but by some class further up in the inheritance tree (like VMobject).

A) throwing an error when one tries to set a parameter that is not existing.

Unfortunately not possible, or at least I can't think of a good way to do it.

B) an easy option to "Reset" the mobject to the default parameters.

This would require additional bookkeeping, in the simplest version we could just store a copy of __init__ as _original__init__ and re-assign it to __init__ if Class.restore_defaults() is called.

C) enabling auto-completion for this change default parameter.

I don't know how autocompletion for keyword parameters works, but I doubt it is possible in this case.

All of this is unfortunate, but at least the error that will be raised if you misspelled a keyword is rather explicit and will point towards the source quickly.

When I would change the color of MathTex, then also the color of Matrix would get changed, as it initializes MathTex.

This is, as you mention, a feature and not a bug. It simply is how inheritance works; if there are unexpected results that come with it, this is on us for not documenting inheritance properly. I don't know how we could change this, even if we wanted to.

@kolibril13
Copy link
Member

What would be the benefit? I think change_default is more explicit and readable.

default is shorter than change_default. But I don't mind too much. Only one other idea: set_default , which might make it a bit more natural to read. MathTex.set_default(color=YELLOW) -> For MathTex, set the default color to yellow.

This would require additional bookkeeping, in the simplest version we could just store a copy of __init__ as _original__init__ and re-assign it to __init__ if Class.restore_defaults() is called.

That would be amazing! Do you have time to add this feature?
Also, maybe a save_state method might be worth it, similar to: https://docs.manim.community/en/stable/reference/manim.mobject.mobject.Mobject.html?highlight=restore#manim.mobject.mobject.Mobject.save_state

This is, as you mention, a feature and not a bug. It simply is how inheritance works; if there are unexpected results that come with it, this is on us for not documenting inheritance properly. I don't know how we could change this, even if we wanted to.

Yep, I also think it is a feature. Looking at: https://docs.manim.community/en/stable/reference/manim.mobject.matrix.IntegerMatrix.html#manim.mobject.matrix.IntegerMatrix
It is not clear that it inits MathTex and also in the inheritance graph, MathTex is at the top right, Matrix is at the button left.
Maybe I can add a sentence inits the caracters with MathTex to the Matrix documentation.
image

Copy link
Collaborator

@k4pran k4pran left a comment

Choose a reason for hiding this comment

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

Really nice feature! Working well for me. Can we also add it to OpenGLMobject please for opengl compatibility?

Interestingly it also works with required args, making them no longer required as you can set their defaults

class RegularPolygramExample(Scene):
    def construct(self):
        RegularPolygram.change_default(num_vertices=6)
        pentagram = RegularPolygram()
        self.add(pentagram)

@behackl
Copy link
Member Author

behackl commented Sep 25, 2021

default is shorter than change_default. But I don't mind too much. Only one other idea: set_default, which might make it a bit more natural to read.

I don't really agree with set_default being more readable than change_default, I think that Circle.change_default(color=GREEN) is just as expressive as Circle.set_default(color=GREEN). Between set and change I prefer change, because set is special as we have the .set mechanism that interfaces methods named set_something, and the default values for keyword arguments don't fit into that framework.

That would be amazing! Do you have time to add this feature?

Sure, I'll figure something out. Instead of introducing a second method, it might be easier to reset the defaults if the method is called without arguments.

Also, maybe a save_state method might be worth it

Maybe as a follow-up if there is interest for it.

It is not clear that it inits MathTex and also in the inheritance graph

You are completely right! Matrix doesn't inherit from MathTex, I misspoke; Matrix "just" uses MathTex (or Tex, but as Tex inherits from MathTex that doesn't really matter) for writing its components. Similar for Integer, or DecimalNumber. But anyhow: there still is not much we can do about it, except document this better.

Really nice feature! Working well for me. Can we also add it to OpenGLMobject please for opengl compatibility?

Yeah, sure. I'll just copy the code over. (In moments like these, I do wish that we had a common abstract base class for both Mobject and OpenGLMobject.)

Interestingly it also works with required args, making them no longer required as you can set their defaults

Yep, this is because the implementation is as silly as it is. Whatever you pass to change_default, it will be added to the __init__ function of the class.

@behackl
Copy link
Member Author

behackl commented Sep 25, 2021

Restoring the original default arguments is now possible by calling change_default without any additional arguments (b34766d), and support for OpenGLMobject is added in e638eaa.

@behackl behackl requested a review from k4pran September 25, 2021 12:30
Copy link
Collaborator

@k4pran k4pran left a comment

Choose a reason for hiding this comment

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

LGTM thanks!

@kolibril13
Copy link
Member

because set is special as we have the .set mechanism that interfaces methods named set_something, and the default values for keyword arguments don't fit into that framework.

Good point, I am convinced that change_default is the way to go!

Maybe as a follow-up if there is interest for it.

I just wanted to mention the idea, I don't find it too important, though.

there still is not much we can do about it, except document this better.

Completely agree on that.

Copy link
Member

@kolibril13 kolibril13 left a comment

Choose a reason for hiding this comment

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

Thanks for adding the restore option!

@WampyCakes
Copy link
Contributor

Great addition!

Can we have a separate file full of defaults being changed like theme.py that we can reference in our manim script? The benefit being orderliness and reusability.

Also, I echo the sentiment of not liking change_default. I don't hate it, but I do kinda prefer .default or even set_default (I don't understand what you were talking about as to why you don't like this option).

Copy link
Member

@Darylgolden Darylgolden left a comment

Choose a reason for hiding this comment

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

This seems like a really great feature, and perfect for implementing themes! No strong opinions either way, but I still slightly prefer set_default over change_default.

@behackl
Copy link
Member Author

behackl commented Sep 25, 2021

Can we have a separate file full of defaults being changed like theme.py that we can reference in our manim script? The benefit being orderliness and reusability.

You can do that, sure! However, this is something users have to do on their own -- we should not encourage anyone to edit the library code.

Also, I echo the sentiment of not liking change_default. I don't hate it, but I do kinda prefer .default or even set_default (I don't understand what you were talking about as to why you don't like this option).

I don't like .default, because it is not very explicit and feels unpythonic. I wouldn't be able to tell easily, what Circle.default(color=RED) does; whereas Circle.set_default(color=RED) or Circle.change_default(color=RED) are much less ambiguous.

As for set_default vs. change_default: I thought that Mobjects had some special magic where a method called set_something is explicitly used as a setter for a property something, and indeed there is something (#995), but it is somewhat different to what I remembered. Long story short, there is no problem (and in particular: no automatism) between the existing set method and a potential set_default method.

It seems pretty much everyone here is in favor of set_default over change_default, so I'll rename it and leave the PR open for a while (in case there are other objections).

Thanks everyone for your feedback!

@behackl behackl changed the title Proof of concept: allow changing default values of keyword arguments Implemented :meth:.Mobject.set_default, a mechanism for changing default values of keyword arguments Sep 26, 2021
@behackl behackl linked an issue Sep 26, 2021 that may be closed by this pull request
@WampyCakes
Copy link
Contributor

You can do that, sure! However, this is something users have to do on their own -- we should not encourage anyone to edit the library code.

I don't mean a theme.py that lives in the library but rather one that lives within the directory of the Manim script (or have a configurable dir). I think part of having a theming setup is being able to use what was done here (adding a set_default method) and making it sharable, reusable, and orderly. This is probably best left for another PR, but I suppose I am advocating for having Manim recognize a theme file just like it does a manim.cfg. With a setup somewhere along those lines, users should be encouraged to take advantage of it (different than what you were thinking before, I know)!

@Darylgolden
Copy link
Member

You can do that, sure! However, this is something users have to do on their own -- we should not encourage anyone to edit the library code.

I don't mean a theme.py that lives in the library but rather one that lives within the directory of the Manim script (or have a configurable dir). I think part of having a theming setup is being able to use what was done here (adding a set_default method) and making it sharable, reusable, and orderly. This is probably best left for another PR, but I suppose I am advocating for having Manim recognize a theme file just like it does a manim.cfg. With a setup somewhere along those lines, users should be encouraged to take advantage of it (different than what you were thinking before, I know)!

Yes, this is within scope of #2015 :)

@icedcoffeeee
Copy link
Collaborator

I'm quite late to the party, but this is a great addition! Especially in support of the theming idea. :))

@kolibril13
Copy link
Member

I think part of having a theming setup is being able to use what was done here (adding a set_default method) and making it sharable, reusable, and orderly. This is probably best left for another PR

We could have a theme gallery similar to this one: https://sphinx-themes.org/
I think a website similar to the mobject gallery would work great for these themes: https://kolibril13.github.io/mobject-gallery/
As I already have the infrastructure, it would not be too much effort to make a theme gallery similarly.

@WampyCakes
Copy link
Contributor

I'd rather people use this feature to develop their own personal styles and use them throughout more than 1 video, but I am not opposed to a theme gallery.

@kolibril13
Copy link
Member

Can we merge this today? :)

@kolibril13
Copy link
Member

I am merging this pr now, so that it is part of the next release.
Looking forward to using this feature, and thanks again for this great addition!

@kolibril13 kolibril13 merged commit c4b96ff into ManimCommunity:main Sep 30, 2021
@behackl behackl added the highlight For contributions that should be highlighted explicitly in the next changelog label Oct 2, 2021
@behackl behackl deleted the change-default branch May 16, 2022 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
highlight For contributions that should be highlighted explicitly in the next changelog new feature Enhancement specifically adding a new feature (feature request should be used for issues instead)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ability to set the default font
6 participants