If you have an android or iphone you've probably seen these, they are trending these days. They are the IN thing, The new boys(or girls :) ) in town.Basically what this widget does is it allows a user to swipe either left or right to view content on the app. This widget is usually displayed when an app is launched(usually for the first time) and contains content about how to use said app or the features of the app
I recently found myself in a situation where I really needed to have those slides in a project I've been hired to do. The reason is simple, The app has a tonne of features and what better way to showcase those features than App Sliders huh?. The great thing about this widget is that you showcase all your app features and teach the user how to use your app simulteneously.Now, if you are using Kotlin and Android Studio, well, this is no big deal as there's a widget for that already, but we are not using Kotlin are we?We are using Python(All the mobile apps I have ever created in my life have been made with python :))
All right Then, Here's what we are going to create
Pretty cool huh?All right, here's the code
First create a file titled uislider.py.And import the things we need
#uislider.py from kivy.uix.boxlayout import BoxLayout from kivy.uix.screenmanager import ScreenManager, Screen, CardTransition
We first import the BoxLayout from kivy's uix repository. We are going to use this as a base for our Slide Content
We then import the ScreenManager and Screen from kivy's uix repository again. These two are the core widget we will use to place and slide content in our demo app
With these two all set, we can start creating our widget. Let's now create widget to hold the slide contents, call it UISlide
#uislider.py ... class UISlide(Screen): def __init__(self, **kwargs): super().__init__(**kwargs) self.__wrapper = BoxLayout(orientation='vertical') self.add_widget(self.__wrapper)
This class is pretty simple, all we did above is:
- Create a widget that inherits from the Screen class
- Add a BoxLayout to our screen which is going to hold all the other widgets in that Slide(by default, the Screen class only shows the first widget added to it so we use the BoxLayout to be able to show more than one widget)
- Finally we just add the BoxLayout to our Slide
Now we need a way to specifically add our widgets on that BoxLayout instead of the Slide itself, so we'll create a small function called add_child to handle that for us.
#uislider.py ... class UISlide(Screen): ... def add_child(self, child): self.__wrapper.add_widget(child)
That does it for our UISLide class. Now let's go ahead and create the main widget in this tutorial: the UISLider class. This is going to be the main class we use to instantiate this widget in our future projects
#uislider.py ... class UISlider(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.__scrnmngr = ScreenManager(transition=CardTransition()) self.add_widget(self.__scrnmngr)
- Created our class UISlider which is essentially just a normal BoxLayout.
- We then create a new ScreenManager instance. This is what we are going to use to Slide our content.
- We finally just add the screen manager to our layout widget.
The ScreenManager only accepts Screen instances hence why we created our UISlide class and now, again, we need a way to add our Slides to the screen manager so we'll create a utility function for that and call it add_slide
#uislider.py class UISlider(BoxLayout): ... def add_slide(self, slide): self.__scrnmngr.add_widget(slide)
All right, the basics are all set, now let's get to the fun stuff. First, let's create a function to handle actually sliding the Slides. We will call this function __slide
#uislider.py class UISlider(BoxLayout): ... def __slide(self, direction): mngr = self.__scrnmngr mngr.transition.direction = direction current = mngr.current_screen if direction == "right": idx = mngr.screens.index(current) if idx == 0: pass else: mngr.current = mngr.previous() else: last = mngr.screens[-1] if current == last: pass else: mngr.current = mngr.next()
Let's go over this function shall we:
- First we simply set the ScreenManager's direction attribute to the swipe direction
- Then, we get the currently visible Slide
- Then we get the index of that Slide relative to other Slides, this is done by utilising the ScreenManager's Screens list index function, which returns the index of a given screen in the screens list
- Next, we check the direction the user is swiping(provided in the function's params). If the user is swiping right, then we check if the current Slide is the first Slide using the Slide's index. If the Slide is indeed the first Slide then obviously there's nothing to slide in that direction so we do nothing. If however it is not the first in the list then we get the previous Slide in our Slides using the ScreenManager's previous() functions which returns the Screen whose index is directly less than the current Screen's index. Then we simply set the current screen of our ScreenManager to that previous Slide
- If, on the other hand, the user is swiping left, then we want to get the last Slide in our Slides.We then check if the current Slide is the last Slide. if it is the last Slide then, again, there's nothing left to slide so we simply ignore the swipe. If however, it is not the last Slide, then we get the next Slide by utilising the ScreenManager's next() function and then we finally set the ScreenManager's current screen to the next Slide
So now we can slide content from any horizontal direction, there's one final problem we need to address: Knowing When The User Is Swiping. As it stands, we just created a function that needs a direction to swipe but we currently do not know which direction to set. Lets fix that. In your UISlider class, overwrite the default on_touch_down event handler with the following content
#uislider.py class UISlider(BoxLayout): ... def on_touch_down(self, touch): if self.collide_point(touch.x, touch.y): touch.grab(self) self.__touch_start = touch.x return True return False
Here's the deal, Everytime you click/touch the screen on your kivy app, an event is fired and the on_touch_down function of all widgets is run, we are going to capitalise on that. What we do here is simple, As soon as a touch/click is detected, we want to check if that click/touch occured inside our widget (self.collide_point()). If it occured inside our widget then naturally, the user was clicking our widget so we want to claim ownership of that touch (touch.grab(self)) and continue. So why bother?Simple, logically speaking the only way to swipe is to first touch right? So we need this event to know when a swipe 'begins'. You'll also notice us assigning self.__touch_start a value. We don't currently have that variable so let's create it in our UISlider class' __init__
#uislider.py class UISlider(BoxLayout): def __init__(self, **kwargs): ... self.__touch_start = None ...
The reason we create this variable is to store the initial position of the touch, we will use it later to determine whether the swipe is going left or right
So now we have a touch/click in our hands, the next thing we want to do is check for a swipe. Now, to swipe we now that a finger has to move from one position on our screen to another without being lifted right?Thankfully kivy also has an event handler for such an occasion. Let's overwrite that event handler with our own code. in our UISlider class
#uislider.py class UISlider(BoxLayout): ... def on_touch_move(self, touch): if self.collide_point(touch.x, touch.y) and touch.grab_current == self: if self.__moved: return if touch.x > self.__touch_start: self.__slide("right") else: self.__slide("left") self.__moved = True
On this function, we again check if the touch occured within the bounds of our widget and also if we actually own this touch touch.grab_current, if it did we check if the currently moving touch is greater than our initial touch. if it is greater then obviously the finger is moving to the right so we call our slide function with the direction set to right otherwise we call the same function with the direction set to left.
Before we do that though, you'll notice that we assess a variable called self.__moved.That variable doesn't yet exist so let's add it real quick to our constructor
#uislider.py class UISlider(BoxLayout): def __init__(self, **kwargs): ... self.__touch_moved = None ...
Remember when I said that everytime a screen is touched/clicked in your application, kivy receives that event? Well, everytime you move your finger in the application kivy receives that event, EVERYTIME. That means that if you swipe your finger from the left of your screen to the end of your screen, our on_touch_move event will be called for about 10-15 times. So we use our self.__moved variable to basically check if we have already received this event. This helps us to categorise this event as a single swipe instead of it being registered as multiple unrelated swipes
Ok, we can now swipe, the next thing to do is do a little cleaning up once the user is done swiping. We will hook on an event again for this. that event is called the on_touch_up event. Let's create it under the on_touch_move event
#uislider.py class UISlider(BoxLayout): def on_touch_up(self, touch): if collide_point(touch.x, touch.y) and touch.grab_current == self: touch.grab() self.__moved = None return True return False
This event handler is fired every time the user lifts their finger from the screen or when you cease clicking on the screen. in our context, it will be fired as soon as the user is done swiping as such:
- We check if we own the current touch
- Then we release that touch since we are done with it
- We then reset our self.__moved variable so we can use it again when the user swipes in the future
And that's it for this widget, let's test it out.Create a new class and call it SliderTest with the following
class SliderTest(BoxLayout): def __init__(self, **kwargs): super().__init__(self, **kwargs) slider = UISlider() slide0 = UISLide() slide1 = UISLide() slide2 = UISLide() im0 = Image(source='/path/to/img') #remember to import the Image class (from kivy.uix.image import Image) im1 = Image(source='/path/to/img') #remember to import the Image class (from kivy.uix.image import Image) im2 = Image(source='/path/to/img') #remember to import the Image class (from kivy.uix.image import Image) slide.add_child(im0) slide.add_child(im1) slide.add_child(im2) slider.add_slide(slide0) slider.add_slide(slide1) slider.add_slide(slide2) self.add_widget(slider)
Finally we make sure our app actually runs
if __name__=="__main__": from kivy.app import App class SliderApp(App): def build(self): return SliderTest() SliderApp().run()
And to run this:
And there you have it, your very own basic ui-slider widget.
And that will be all for this article, tell me what you think about this widget in the comments below. Happy Coding Y