omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    [share code] SliderWithLabel class for ui.Slider() featuring editable label

    Pythonista
    6
    27
    19876
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • cook
      cook last edited by cook

      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 value attribute based on a user-set max_val argument.
      • Setting 'max_val' will create a SliderWithLabel.value based on slider.value * max_val. It will return a rounded integer. A normal slider has a value between 0 and 1.0. max_val gives convenience. Default is 100.
      • can set the value for the default display value. Default is 50 (Half)
      • can set the tint_color for the bar. Default is 0.7 so 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 label with the usual style attributes (background_color etc)

      Code is here

      1 Reply Last reply Reply Quote 2
      • Webmaster4o
        Webmaster4o last edited by

        Only issue is you can't do things like set the frame on your class. I'd look into using ProxyTypes

        1 Reply Last reply Reply Quote 1
        • cook
          cook last edited by

          @Webmaster4o yes that's true. I think though I could easily add it in with the kwargs. I don't know if there's a point to use another module. I'll try and update it.

          One thing that I would like to add to this is a delegate for value_did_change so that you could use that outside the class.

          But- adding delegates is still something I haven't tried much...

          1 Reply Last reply Reply Quote 0
          • filippocld
            filippocld last edited by

            I modified in my script this line to set the label textcolor. I think it is easier to use this than sliderclass.label.text_color

            self.label.text_color = kwargs['text_color'] if 'text_color' in kwargs else 0.7
            
            1 Reply Last reply Reply Quote 1
            • cook
              cook last edited by

              Added ability to set frame as a keyword argument. Thanks @Webmaster4o

              @filippocld thanks... Also changed that bit.

              At first I had just a UI.Label and had everything changeable.. But after switching to the textfield, I couldn't change much so I had taken out all of the kwargs - border color, background color etc.
              It's a bit unfortunate that you can't change the background color (easily). It seems it can be done by changing the image of the background with objc. It's almost just too much hassle to both... Especially if we want different colors. Would have to make an image, then do some objc magic. I don't get paid enough by @Webmaster4o to attempt that sort of stuff.

              1 Reply Last reply Reply Quote 0
              • cook
                cook last edited by

                Now also has a delegate

                1 Reply Last reply Reply Quote 0
                • JonB
                  JonB last edited by

                  To make this as generic as possible, You might consider, rather than just a max_value, to have a limits tuple, that lets you specify the min and max values.
                  value=limits[0] + slider.value*(limits[1]-limits[0])

                  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...

                  1 Reply Last reply Reply Quote 0
                  • cook
                    cook last edited by

                    @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: (value, name))

                    What's the best approach?

                    Let's say I have a class with three sliders and set the delegate to self for each one and provide the method... If I can distinguish value based on name (or something) then it's probably easier than creating 3 separate objects just for delegate use.

                    Phuket2 1 Reply Last reply Reply Quote 0
                    • ccc
                      ccc last edited by

                      @filippocld

                      self.label.text_color = kwargs.get('text_color', 0.7)
                      
                      1 Reply Last reply Reply Quote 0
                      • Phuket2
                        Phuket2 @cook last edited by

                        @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')
                        
                        1 Reply Last reply Reply Quote 0
                        • filippocld
                          filippocld last edited by

                          @ccc Yeah, i just followed the style that @cook used

                          1 Reply Last reply Reply Quote 0
                          • Phuket2
                            Phuket2 last edited by

                            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__)
                            
                            1 Reply Last reply Reply Quote 0
                            • cook
                              cook last edited by

                              @filippocld and now that I saw what @ccc put I remember that way of doing it and it is much better. Had forgotten. Will change it next update! (Thanks @ccc ...!)

                              1 Reply Last reply Reply Quote 0
                              • JonB
                                JonB last edited by

                                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)

                                1 Reply Last reply Reply Quote 1
                                • Webmaster4o
                                  Webmaster4o last edited by

                                  @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

                                  1 Reply Last reply Reply Quote 2
                                  • cook
                                    cook last edited by

                                    @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!

                                    1 Reply Last reply Reply Quote 0
                                    • JonB
                                      JonB last edited by

                                      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.

                                      1 Reply Last reply Reply Quote 0
                                      • cook
                                        cook last edited by

                                        @jonb thank you- will try later. It's really late for me now so I will just sleep!

                                        1 Reply Last reply Reply Quote 0
                                        • cook
                                          cook last edited by

                                          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.

                                          Phuket2 1 Reply Last reply Reply Quote 0
                                          • Phuket2
                                            Phuket2 @cook last edited by

                                            @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

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors