[share code] SliderWithLabel class for ui.Slider() featuring editable label
This is a wrapper for
ui.Slider()that shows a label above the slider thumb with the current value (it slides along). Here are some features this provides:
- The displayed label is editable (it is a
textfield) and entering a new number updates the slider position
- Has a custom
valueattribute based on a user-set
- Setting 'max_val' will create a
max_val. It will return a rounded integer. A normal slider has a value between 0 and 1.0.
max_valgives convenience. Default is
- can set the
valuefor the default display value. Default is
- can set the
tint_colorfor the bar. Default is
0.7so there is no color.
- set the frame for SliderWithLabel works well.
- remember that this needs a height of 60!! It's a lot, I know. But don't worry about it!! Make everything else in your view smaller!!
- changed `delegate is gone in favour of being able to assign an action.
- added can now style the
labelwith the usual style attributes (background_color etc)
- The displayed label is editable (it is a
To make this as generic as possible, You might consider, rather than just a max_value, to have a
limitstuple, that lets you specify the min and max values.
value=limits + slider.value*(limits-limits)
Another idea: perhaps, a user specified number to string translation function, or format string. Basically let the user specify the conversion from a string repr to the value, and vice versa. That would enable hexidecimal entry, floats, rounded ints, rounded to the nearest tenth, hundredth, etc...
@jonb thanks- will try to add in a min value. Makes sense.
Yes the different rounded would be good too, I realize that some would use sliders for crazy numbers.
Also- I think that it may be necessary to make a way for the delegate to distinguish values from different sliders (if they are using the same delegate, for example). I don't know if that's a good way or just creating different delegates per slider. Could easily add a name for the delegate (passing a tuple:
What's the best approach?
Let's say I have a class with three sliders and set the delegate to
selffor each one and provide the method... If I can distinguish
name(or something) then it's probably easier than creating 3 separate objects just for delegate use.
self.label.text_color = kwargs.get('text_color', 0.7)
@cook , thanks for sharing again. I really like the effect. It strikes me that you should maybe you should have a name associated with the slider and also pass that back via the delegate. As it would be normal to use one delegate for a number of sliders as you demonstrate in your example.
I am not trying to be critical, but it does look a little messy. I hope you don't mind me saying so. I say it with the best of intentions. Below is a skeleton I normally follow. Not sure it's great, probably better ways to do it.
But it does make it a bit more clearer to what's happening by splitting it up,a little. You also seem to be use a lot of literal values.
Anyway, I really like what you have done.
import ui class MyClass(ui.View): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # def instance vars self.slider = None #etc... self.make_view(**kwargs) def make_view(self, **kwargs): slider = ui.Slider() slider.bg_color = 'green' #etc self.add_subview(slider) self.slider = slider def layout(self): r = ui.Rect(*self.bounds.inset(5, 5)) self.slider.width = r.width self.slider.x = r.x self.slider.y = r.y def draw(self): # only should overide draw if you acually need to use it # here only illustration purposes. pass if __name__ == '__main__': v = MyClass(frame = (0, 0, 600, 800), bg_color = 'white') v.present('sheet')
This is a nice quick way to pull out kwargs that are not part of the ui.View for example. I have seen this done before. I think @Webmaster4o , did something like this before. But you can imagine versions of it. The nice thing is if you pull out kwargs of interest, then you can apply the attrs in a loop rating than having to reference them directly.
Of course does not need to be in its own function.
# returns any kwargs that are not attrs in ui.View def get_non_uiview_kwargs(self, **kwargs): return set(kwargs) - set(ui.View.__dict__)
grnerally with ui delegates or actions, the first argument is the ui component, so that you can have the same callback serve multiple components. To be consistent with the way ui works, you should just pass the slider object itself, (a.k.a self)
For a slider, you should simply have one argument: slider. The user can then query the value.
if you had an action which gets called before the value is i
set ( such as slider_should_change), in that case you'd also send the value, and expect a return)
@ccc is really good at spotting redundant logic. I don't know anyone else who can do that like he does. The other day, I had code that said something like
x if x else y, and @ccc spotted that it could be simplified to
x or y. I never catch these things. Thanks, @ccc :D
@jonb thank you. I've made that change so the delegate receives
self. Also saw your note re: setting an action rather than a delegate. I think this is a much better way than a delegate (as you've said). Anyway.. How is this done? Do you have a simplified example so I could see what it's like? I know that you can code it in on this script no problem...- for me it's a learning opportunity as well. So- seeing a simplified example with some explanation can help. Some of these things go over my head simply because I have never learned about them yet.
Thanks for your suggestions and help!
an action would be similar to how you use the delegate, except you are checking for if self.action and callable(self.action). just set self.action=None in the constructor. you could even support both delegates and actions, which is how several ui components work.
@jonb thank you- will try later. It's really late for me now so I will just sleep!
Thanks @jonb. I've actually removed the delegate bit in favour of setting an action.
Also updated with a bit more functionality: ability to set style (and other) attributes of the label.
Next up: min/max value setting and different style of numbers.
@cook , I am not sure I agree with @jonb totally. However, he has whipped me many times. Everytime I contradict him, I am sure I will be wrong.
But regardless.what I would say, some times actions get buried in layers of code. Sometime it's hard to make sense where you should define all this. On the other hand a delegate object can be very flexible in its implementation/design usage. Sorry, maybe some wrong language here. But a delegate object is very flexible in my mind vrs a action. Maybe the word I am looking for is scope.
As usual, I am not sure. It could be a lack of ability that makes actions less attractive/usable to me. Sure a button click only needs a action. But even on bigger projects, not just some test code, this can get difficult.
Anyway, my 2 cents worth of info. I don't say it to try to be smarter, I am just not smarter than these other guys. I still have to say what I think
@phuket2 I think just having an action is more in-line with how the ui.Slider() works so it seems more appropriate to me. I don't imagine there's much more that one would want to do and effectively this action does what the delegate was doing.
(Also... A little less code to implement an action!!)
@cook , I will capitulate. But, I still believe in what I say. But it will not be proven with some test code. You need to implement it in a multi view app before you will start to see the limitations or restrictions. Sample code is just so different. You can make everything work in sample code somehow. But as you start to build a app, things change. Scope/reachablilty starts becoming big issues. I mean to not have to cludge connections between objects. Again this goes back to MCV (MODEL, CONTROLLER,VIEW) or close to that.
But I will stand corrected if need be
I say, try to match the interface if the thing you are replicating. ui.Slider has
actionso an action will be what someone expects.
There is also nothing wrong with using both a delegate and an action -- such as
ui.TextView, which have an action which is called for the most common delegate item, along with delegate items. Both can be set.
IMHO a delegate makes sense when there are more than one type of callback, or when a callback needs other info instead of just
sender. But for single action delegates, an action is easier to work with, since it requires less code, and is more easily discoverable (how many times have you had to pull up the docs to remember whether it is
@cook , ok. I am a little drunk now. But, on a single view, a action seems exactly the correct choice.
But I am just trying to differentiate between test code and actual implementation of a full application.
I have tried it many times, where objects are out of reach when you start to make a functional application.
I am not saying I am right. Because I have been proven wrong many times. Really, I am not trying to argue. I am just saying as you start to implement a application comprised of many views/subviews it gets a lot harder to keep in control of the scope of your objects in a meaningful way.
But I will stop now. As I say, is just my opinion. 😜🎉🎉🎉
@phuket2 I understand the point you are trying to make. My thoughts are just the same as @jonb has said. If I had a dollar for every time I pushed the 'copy' button on top of the tableview delegates code in the docs.... For now I'll just leave an action. I don't think there's a need for any other callbacks. If there was a delegate I think at most we have four options:
label_should_enter_value(return true or false)
slider_should_slide(return true or false)
But option three and four are also possible to implement just with some keyword args. Option one and two just seem the same overall. I think in the end you just get a bit of overkill implementing a delegate when most likely people just need to get the
sender.valuewhich is easier to implement with the
Anyway... I have some thoughts about MVC ... not specific to this but it got me thinking. When I have some time to write them down a bit I'll share with you.