-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Carousel: Make 'Carousel' able to handle the case where 'loop == True' and 'len(slides) == 2' #6374
Conversation
kivy/data/style.kv
Outdated
@@ -538,6 +538,10 @@ | |||
BoxLayout: | |||
id: container | |||
|
|||
|
|||
<Carousel>: | |||
_prev_equals_next: self.loop and (len(self.slides) == 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would discourage using KV for things like this. KV should be reserved for actual styling rules that doesn't set the widget state, but just styles it based on the widget state and not for program logic. Adding program logic to KV makes the programs harder to trace as it violates the action at a distance principle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like using KV like this because it's much simpler compared to doing the same thing in python. But if you don't think so, I don't do this anymore on this project.
Regardless of that, I'll remove that part because I'll make _prev_equals_next
into non KivyProperty as you suggested.
kivy/uix/carousel.py
Outdated
@@ -284,6 +274,8 @@ def _next_slide(self): | |||
_next = ObjectProperty(None, allownone=True) | |||
_offset = NumericProperty(0) | |||
_touch = ObjectProperty(None, allownone=True) | |||
_prev_equals_next = BooleanProperty(False) | |||
_prioritize_next = BooleanProperty(False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless the properties are needed for the users to be able to bind to, or it really simplifies the code, it is better to use a @property
or normal python property rather than a KivyProperty.
That's because with Kivy properties it is harder to trace the code logic (due to action at a distance), and they add loading performance cost to the widgets.
So for example, _prev_equals_next
doesn't seem to benefit at all from being a KivyProperty, so it should just be a normal property. But even _prioritize_next
, I would make it a normal property and inline the code in on__prioritize_next
at the one place you set it. Or you can make it a python @property
with a setter that does the stuff currently in on__prioritize_next
when set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you. I'll make those into non KivyProperty.
kivy/uix/carousel.py
Outdated
@@ -446,12 +450,19 @@ def on_index(self, *args): | |||
self._trigger_position_visible_slides() | |||
self._offset = 0 | |||
|
|||
def on_loop(self, *args): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is generally better to directly fbind
_insert_visible_slides
to loop
in the __init__
method, than to use the on_loop
method. I know this principle is frequently violated in the Kivy codebase (even in on_slides
below), but I try to fix it when I happen to work on code that does this.
The problem with this pattern is that if a user inherits and overwrites the on_xxx
method for their own reason, we lose all the stuff that we wanted to happen as a result to xxx
changing. So it's better to bind directly. For this case you'd need to bind a local function that eats the args
passed from the callback, but it's still better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I understand what you mean. I'll fix that.
kivy/uix/carousel.py
Outdated
if self._prev_equals_next != expected_value: | ||
from kivy.logger import Logger | ||
Logger.error( | ||
"Carousel: I expected '_prev_equals_next' to be already " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This smells to me like it should be an assert
. Logging errors is generally discouraged because it trains people to ignore them, and if this path is hit, users would see an error without much for them to do. This seems like if this path is hit it would be a bug in kivy if I read this correctly, hence an assert
is more appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be related to the order of evaluation. I mean things like this:
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.properties import StringProperty
class TestEvaluationOrder(Factory.Widget):
prop1 = StringProperty()
prop2 = StringProperty()
def apply_class_lang_rules(self, *args, **kwargs):
def handler(c, self, *args):
print(c, ':', self.prop2 == self.prop1 + ' prop2')
self.fbind('prop1', handler, 'A')
r = super().apply_class_lang_rules(*args, **kwargs)
self.fbind('prop1', handler, 'B')
return r
KV_CODE = '''
<TestEvaluationOrder>:
prop2: self.prop1 + ' prop2'
TestEvaluationOrder:
'''
w = Builder.load_string(KV_CODE)
w.prop1 = 'prop1'
A : False
B : True
Logging errors is generally discouraged because it trains people to ignore them
got it
Thanks for your reviews as always |
I didn't test, but the changes look good. Let me know when you think it's ready to merge by removing WIP from the title. |
9506778
to
1d2ce89
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't test, but it looks ok. The travis CI osx failures are unrelated.
When a
Carousel
meets the following conditions:carousel.loop == True
len(carousel.slides) == 2
it doesn't work properly, e.g. like this. This is probably because the next slide and the previous slide are the same under that conditions.
The tests for this PR aren't fully written because I don't have enough knowledge to automate GUI. Instead I'm using this, and testing manually.