Welcome!

Sign in with your CogSci, Facebook, Google, or Twitter account.

Or register to create a new account.

We'll use your information only for signing in to this forum.

Supported by

Vertical Touchscreen Slider with Confirm Button

kezzo2002kezzo2002 Posts: 10

I realise there have been quite a lot of threads on the implementation of sliders & their use on touch screens. So sorry to add another! But I have read through these and had lots of trial and error - but as a beginner my lack of python understanding is hindering me from achieving my desired outcome. I am hoping someone can help...

What I am trying to achieve is a vertical slider that works on a touchscreen with an image at the top and bottom, that when participants press or slide their finger up and down the bar/line it fills (or a marker moves up and down - I'm not too fussy on the aesthetics). As with the other threads - I'd prefer it if this only occurs within the area of the slider not just anywhere on the screen. Then, when participants are happy with their response, I'd like there to be a confirm button for them to press that then logs their response. I'm pretty sure much of the code has been presented on the threads I've looked at - but for the life of me I can't piece it all together!

Thanks in advance for any guidance...

Comments

  • sebastiaansebastiaan Posts: 2,670

    Hi,

    The existing solutions are indeed not entirely satisfying. So let's see how you can do this more elegantly, by adding a slider widget to the forms. The following code will inject a slider into the form widgets, meaning that from then on you can use this slider just like any of the other widgets. Ideally, you would do this in the Prepare phase of an inline_script at the very start of the experiment.

    This particular variation of the slider simply registers a value between 0 and 1, and shows this as a vertically filled bar (empty = 0, filled = 1).

    from libopensesame.widgets._widget import widget
    from libopensesame import widgets
    
    
    class slider(widget):
    
        def __init__(self, form, var=None):
    
            widget.__init__(self, form)
            self._fill = .5
            self.var = var
            self.set_var(self._fill)
    
        def render(self):
    
            x, y, w, h = self.rect      
            self.draw_frame(rect=(x,y+h*(1-self._fill),w,h*self._fill))
    
        def on_mouse_click(self, pos):
    
            x, y, w, h = self.rect
            yclick = pos[1]
            dclick = yclick-y
            drect = h       
            self._fill = min(1, max(0, 1-dclick/drect))
            self.set_var(self._fill)
    
    
    widgets.slider = slider
    

    And then you can simply use the slider as a widget in a form_base, like below. This snippet will show a slider at the top and a button at the bottom to close the form. Not the most elegant of configurations, but it shows the general idea.

    widget 0 0 2 1 slider var=response
    widget 0 1 2 1 button
    

    Hope this helps!

    Cheers,
    Sebastiaan

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • kezzo2002kezzo2002 Posts: 10

    Hi Sebastiaan,

    Thank you for this. This looks like a much better solution moving forward - however when I paste the code above into the prepare section (or the run section) of an inline script at the beginning of my experiment, and then later on add a new form base with the code you suggest - I get a grey box above a button.... like it doesn't recognise that there is now a new slider widget...

  • sebastiaansebastiaan Posts: 2,670

    I get a grey box above a button.... like it doesn't recognise that there is now a new slider widget...

    Try clicking ont the grey box. :wink: The slider adjusts to the form geometry, so if you adjust the geometry such that the slider isn't a box but an elongated vertical rectangle, it will look more like a slider.

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • kezzo2002kezzo2002 Posts: 10

    Ah! I see.... Thank you! With a bit of tinkering to make it look pretty this will work a treat.... (I will post the code when I have done so in case of use to others!)

    One thing though... on a touchscreen this doesn't truly slide when you move your finger - only when you press in different places (presumably because its recognising clicks rather than drags/mouse-movement). The sliders on earlier discussion threads did this quite well (although they couldn't be added as a widget like this which I think is much better)...Is it possible to achieve this sliding movement - within the confines of the slider area on the form?

  • sebastiaansebastiaan Posts: 2,670

    .Is it possible to achieve this sliding movement - within the confines of the slider area on the form?

    Partly, yes. OpenSesame's mouse object doesn't understand mouse-up events, so to implement this you need to access the underlying libraries directly. Below is a slight extension of the above slider that does so but only for the back-ends that rely on pygame (right now all except psycho).

    from libopensesame.widgets._widget import widget
    from libopensesame import widgets
    from openexp._mouse.legacy import legacy        
    
    
    class slider(widget):
    
        def __init__(self, form, var=None):
    
            widget.__init__(self, form)
            self._fill = .5
            self.var = var
            self.set_var(self._fill)
            self._mouse = mouse()           
            self._use_pygame = isinstance(self._mouse, legacy)
    
        def render(self):
    
            x, y, w, h = self.rect      
            self.draw_frame(rect=(x,y+h*(1-self._fill),w,h*self._fill))
    
        def on_mouse_click(self, pos):
    
            return self._pygame(pos) if self._use_pygame else self._basic(pos)
    
        def _basic(self, pos):
    
            x, y, w, h = self.rect
            yclick = pos[1]
            dclick = yclick-y
            drect = h       
            self._fill = min(1, max(0, 1-dclick/drect))
            self.set_var(self._fill)
    
        def _pygame(self, pos):
    
            import pygame
    
            self._basic(pos)
            while True:
                for event in pygame.event.get():
                    if event.type == pygame.MOUSEBUTTONUP:
                        return
                    if event.type != pygame.MOUSEMOTION:
                        continue
                    pos = self._mouse.get_pos()[0]
                    self._basic(pos)
                    self.form.render()
    
    
    widgets.slider = slider
    
    Thanked by 1kezzo2002

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

  • kezzo2002kezzo2002 Posts: 10

    This works beautifully! Thanks ever so much. A final side note - if I wanted a frame/box round the slider area (so min and max were clearer) - is the best thing to add into the above code instructions to draw a box, or to add an image of a box to the same space on the custom form?

  • sebastiaansebastiaan Posts: 2,670
    edited July 11

    if I wanted a frame/box round the slider area (so min and max were clearer) - is the best thing to add into the above code instructions to draw a box, or to add an image of a box to the same space on the custom form?

    There are various ways to do this, but for maximum flexibility, you could customize slider.render() even further and directly draw to the form's canvas. For example like so:

        def render(self):
    
            x, y, w, h = self.rect                      
            # Slider
            self.form.canvas.rect(x,y+h*(1-self._fill),w,h*self._fill, fill=True,
                color='gray')
            # Outline
            self.form.canvas.rect(x, y, w, h, penwidth=4, color='white')
    
    Thanked by 1kezzo2002

    There's much bigger issues in the world, I know. But I first have to take care of the world I know.
    cogsci.nl/smathot

Sign In or Register to comment.