Kivy Image Buttons Are Shaped Like... Lemons? - kivy

I'm trying build a GUI in python with an array of buttons and some circular menu items on the right. I made a some png files for the menu items, but when I add them as buttons into the code, it shows them kind of like two lemons on top of each other rotated 90˚. A bit like this: Lemon Buttons
These are the icons that I made:
Circular Buttons
Here is the code I have right now.
from kivy.app import App
from kivy.uix.button import Button
from kivy.config import Config
from kivy.uix.floatlayout import FloatLayout
buttonSize = (0.3, 0.2)
Config.set('graphics', 'resizable', True)
class MyApp(App):
def build(self):
layout = FloatLayout()
button1 = Button(
text='1st Function',
size_hint=buttonSize,
pos_hint={'x':.05, 'y':.75}
)
button2 = Button(
text='2nd Function',
size_hint=buttonSize,
pos_hint={'x':.4, 'y':.75}
)
button3 = Button(
text='3rd Function',
size_hint=buttonSize,
pos_hint={'x':.05, 'y':.475}
)
button4 = Button(
text='4th Function',
size_hint=buttonSize,
pos_hint={'x':0.4, 'y':0.475}
)
button5 = Button(
text='5th Function',
size_hint=buttonSize,
pos_hint={'x':.05, 'y':.2}
)
button6 = Button(
text='6th Function',
size_hint=buttonSize,
pos_hint={'x':0.4, 'y':0.2}
)
menuButton = Button(
background_normal='Circular Button.png',
background_down='Circular Button Down.png',
size_hint=(0.125, 0.15),
pos_hint={'x':0.8, 'y':0.8}
)
patientButton = Button(
background_normal='Patient Button.png',
background_down='Patient Button Down.png',
size_hint=(0.125, 0.15),
pos_hint={'x':0.8, 'y':0.5}
)
layout.add_widget(button1)
layout.add_widget(button2)
layout.add_widget(button3)
layout.add_widget(button4)
layout.add_widget(button5)
layout.add_widget(button6)
layout.add_widget(menuButton)
layout.add_widget(patientButton)
return layout
if __name__ == '__main__':
app = MyApp()
app.run()
How do I get the icons to show normal circles instead of these monstrosities?

This seems to be caused by the (lack of) border attribute.
Fix it by setting borders to 0 as follows:
menuButton = Button(
background_normal='Circular Button.png',
background_down='Circular Button Down.png',
size_hint=(0.1, 0.1),
pos_hint={'x':0.8, 'y':0.8},
border=(0, 0, 0, 0)
)
patientButton = Button(
background_normal='Patient Button.png',
background_down='Patient Button Down.png',
size_hint=(0.2, 0.2),
pos_hint={'x':0.8, 'y':0.5},
border=(0, 0, 0, 0)
)
Result (images are not perfectly square and I set second one to twice the size hint, this is just for testing):

Related

How to implement sliding animation for selector in Jetpack Compose?

I have the following code:
#Composable
internal fun ActivityInputBottomSheet() {
val items = listOf("Calories", "Steps", "Water")
var currentActive by remember {
mutableStateOf(items[0])
}
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier
.clip(CircleShape)
.background(Color.Red),
verticalAlignment = Alignment.CenterVertically
) {
items.forEach {
Text(
it,
modifier = Modifier
.clip(CircleShape)
.background(if (it == currentActive) Color.Blue else Color.Transparent)
.clickable {
currentActive = it
}
.padding(
vertical = Padding.Padding8,
horizontal = Padding.Padding16
),
color = Color.White,
)
}
}
}
}
Here I implemented a selector with 3 options (when user clicks at the one option it becomes active, and all other become inactive). It works correctly, but I want to add animation of sliding from the one option to another while changing state of selector (for example if user clicks at the first option and then at the third, blue background will slide through the first, then second and stop at third option). How can it be achieved? Thanks in advance for any help!

wxPython - Can't make wx.StaticText align inside a wx.StaticBoxSizer

how it going?
I'm trying to align two wx.StaticText inside a horizontal box, which is inside a vertical wx.StaticBoxSizer. See for yourself. I guess the code says what I'm trying to do.
Thanks!
import wx
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
sizer = wx.StaticBoxSizer(wx.VERTICAL, self)
left = wx.StaticText(self, -1, 'left', style=wx.ALIGN_LEFT)
right = wx.StaticText(self, -1, 'right', style=wx.ALIGN_RIGHT)
hBox = wx.BoxSizer(wx.HORIZONTAL)
hBox.Add(left)
hBox.Add(right)
sizer.Add(hBox)
self.SetSizerAndFit(sizer)
app = wx.App()
frame = Frame(None).Show()
app.MainLoop()
Ever since the removal of wx.ALIGN_LEFT and wx.ALIGN_RIGHT in horizontal boxsizers and the introduction of:
wx._core.wxAssertionError: C++ assertion "!(flags & wxALIGN_RIGHT)" failed at /tmp/pip-install-8ko13ycp/wxpython/ext/wxWidgets/src/common/sizer.cpp(2168) in DoInsert(): Horizontal alignment flags are ignored in horizontal sizers,
it's an issue.
Another option is to use the spacing in the sizer instruction to the Left and/or Right. e.g.
import wx
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
sizer = wx.StaticBoxSizer(wx.VERTICAL, self, "Test")
left = wx.StaticText(self, -1, 'left')
right = wx.StaticText(self, -1, 'right')
hBox = wx.BoxSizer(wx.HORIZONTAL)
hBox.Add(left, 0, wx.RIGHT, 100)
hBox.Add(right, 0, wx.LEFT, 100)
sizer.Add(hBox)
self.SetSizerAndFit(sizer)
app = wx.App()
frame = Frame(None).Show()
app.MainLoop()
It seems I have to specify a size to make the aligment work!
import wx
class Frame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
sizer = wx.StaticBoxSizer(wx.VERTICAL, self)
left = wx.StaticText(self, -1, 'left', size=(175, 15), style=wx.ALIGN_LEFT)
right = wx.StaticText(self, -1, 'right', size=(25, 15))
hBox = wx.BoxSizer(wx.HORIZONTAL)
hBox.Add(left)
hBox.Add(right)
sizer.Add(hBox)
self.SetSizerAndFit(sizer)
app = wx.App()
frame = Frame(None).Show()
app.MainLoop()

How to delete clicked Widget?

I create child widgets (Buttons for example).
When I click each button (each child widget) I wish to remove the clicked widget from parent.children.
I can't figure out how to fetch that specific clicked widget from parent.children other than having a clumsy system in parent:
def DeleteWidget(self, targetWidget):
for i in self.children:
if id(i) == id(targetWidget):
self.remove_widget(i)
Any better methods to delete a clicked widget?
You can bind each button's on_press property to your class method DeleteWidget like this:
btn1 = Button(text= "Button 1")
btn1.bind(on_press = self.DeleteWidget)
When btn1 is pressed, it'll execute your DeleteWidget method which will take an argument (in this case, button object with its address).
def DeleteWidget(self, *args):
self.remove_widget(args[0])
Here's a basic example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import *
from kivy.uix.boxlayout import *
class TestApp(App):
def build(self):
bx = BoxLayout(orientation = 'vertical')
btn1 = Button(text="Button 1")
btn1.bind(on_press = self.deleteButton)
bx.add_widget(btn1)
btn2 = Button(text="Button 2")
btn2.bind(on_press = self.deleteButton)
bx.add_widget(btn2)
return bx
def deleteButton(self, x):
self.root.remove_widget(x)
TestApp().run()

Using Kivy's Settings as a Screen

I'm trying to create an application with a Settings screen (in a ScreenManager). How do I place the Settings into a Screen so I can place the Screen into a ScreenManager so when the Screen is navigated to the Settings instance is rendered, then is hidden with it is navigated away from it?
Here is a basic example on how to use screenmanager with 2 screens.
from kivy.app import App
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
class ScreenOne(Screen):
def __init__(self,**kwargs):
super(ScreenOne,self).__init__(**kwargs)
self.box = BoxLayout(orientation='vertical')
self.label1 = Label(text="One label on screen 1")
self.label2 = Label(text="Another label label")
self.button = Button(text="Go to screen 2")
self.button.bind(on_press=self.change_screen)
self.box.add_widget(self.label1)
self.box.add_widget(self.label2)
self.box.add_widget(self.button)
self.add_widget(self.box)
def change_screen(self,*args):
self.manager.current = 'screen2'
class ScreenTwo(Screen):
def __init__(self,**kwargs):
super (ScreenTwo,self).__init__(**kwargs)
self.box = BoxLayout(orientation='vertical')
self.label1 = Label(text="One label on screen 2")
self.label2 = Label(text="Another label label")
self.button = Button(text="Go to screen 1")
self.button.bind(on_press=self.change_screen)
self.box.add_widget(self.label1)
self.box.add_widget(self.label2)
self.box.add_widget(self.button)
self.add_widget(self.box)
def change_screen(self,*args):
self.manager.current = 'screen1'
class MyApp(App):
def __init__(self,**kwargs):
super(MyApp,self).__init__(**kwargs)
self.sm = ScreenManager()
self.sm.add_widget(ScreenOne(name = "screen1"))
self.sm.add_widget(ScreenTwo(name = "screen2"))
def build(self):
return self.sm
if __name__ == "__main__":
MyApp().run()

Kivy: Swiping (Carousel & ScreenManager)

I have two Screens in a ScreenManager that both contains a number of buttons in a ScrollView. The idea is that one steps forward (right) by clicking a button. And step back (left) by swiping back. So I am trying to add a Carousel to implement that one swipe on the second page. This is what I have tried:
self.root = ScreenManager(id = 'screen_manager')
main_screen = Screen(name = 'main_screen')
scroller = Scroller()
button_text = ['teach', 'move', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8']
for text in button_text:
scroller.view.add_widget(Field(name=text, direction='left', current='teach'))
main_screen.add_widget(scroller)
self.root.add_widget(main_screen)
carousel = Carousel(direction='left', id='carousel')
teach = Screen(name = 'teach')
scroller2 = Scroller()
button_text = ['vocab', 'drills']
for text in button_text:
scroller2.view.add_widget(Field(name=text, direction='right', current='main_screen'))
carousel.add_widget(scroller2)
teach.add_widget(carousel)
self.root.add_widget(teach)
But since I have only added the second Screen, it's not possible to swipe in either direction. Carousel's load_slide() method takes a slide as argument. Assuming by slide they mean a Carousel. Given I am going to have a lot pages, I probably need the Carousel to be loaded dynamically, using add_widget() and remove_widget(). Would appreciate some pointers.
Working example of the code I have so far: http://dpaste.com/33464R2
You can do this by using ScreenManager and Gestures. (../kivy/examples/gestures/)
See here kivy-github-gestures
I have explained everything in the code in comments.
First you need create a new Python file named gesture_box.py.
gesture_strings copy from here
from kivy.gesture import GestureDatabase
from kivy.uix.boxlayout import BoxLayout
from kivy.gesture import Gesture
[Paste gesture_strings here]
#This database can compare gestures the user makes to its stored gestures
#and tell us if the user input matches any of them.
gestures = GestureDatabase()
for name, gesture_string in gesture_strings.items():
gesture = gestures.str_to_gesture(gesture_string)
gesture.name = name
gestures.add_gesture(gesture)
class GestureBox(BoxLayout):
def __init__(self, **kwargs):
for name in gesture_strings:
self.register_event_type('on_{}'.format(name))
super(GestureBox, self).__init__(**kwargs)
def on_left_to_right_line(self):
pass
#To recognize a gesture, you’ll need to start recording each individual event in the
#touch_down handler, add the data points for each call to touch_move , and then do the
#gesture calculations when all data points have been received in the touch_up handler.
def on_touch_down(self, touch):
#create an user defined variable and add the touch coordinates
touch.ud['gesture_path'] = [(touch.x, touch.y)]
super(GestureBox, self).on_touch_down(touch)
def on_touch_move(self, touch):
touch.ud['gesture_path'].append((touch.x, touch.y))
super(GestureBox, self).on_touch_move(touch)
def on_touch_up(self, touch):
if 'gesture_path' in touch.ud:
#create a gesture object
gesture = Gesture()
#add the movement coordinates
gesture.add_stroke(touch.ud['gesture_path'])
#normalize so thwu willtolerate size variations
gesture.normalize()
#minscore to be attained for a match to be true
match = gestures.find(gesture, minscore=0.3)
if match:
print("{} happened".format(match[1].name))
self.dispatch('on_{}'.format(match[1].name))
super(GestureBox, self).on_touch_up(touch)
Now create your main.py file.
import gesture_box as gesture
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class Runner(gesture.GestureBox):
pass
class MainApp(App):
def build(self):
return Runner()
if __name__ == '__main__':
app = MainApp()
app.run()
And your main.kv file
<Runner>:
#Handling the gesture event.
on_left_to_right_line: manager.current = 'main_screen'; manager.transition.direction = 'right'
ScreenManager:
id: manager
Screen:
name: "main_screen"
BoxLayout:
orientation: 'vertical'
Label:
Button:
text: "Child 1"
on_release:
manager.current = "child1"
manager.transition.direction = 'left'
Label:
Button:
text: "Child 2"
on_release:
manager.current = "child1"
manager.transition.direction = 'left'
Label:
Screen:
name: "child1"
Label:
text: "Swipe from left to right to go to main screen (CHILD 1)"
Screen:
name: "child2"
Label:
text: "Swipe from left to right to go to main screen (CHILD 1)"
EDIT: Many people have asked me that how these gesture string are generated.
Kivy guys provide this in their examples.
see here https://github.com/kivy/kivy/blob/master/examples/gestures/gesture_board.py
run this file.
python gesture_board.py
It should open a blank window.
make a gesture on it using mouse or touch.
When the on_touch_up event is triggered, It will output the gesture string in the terminal.
for example the output for right_to_left_line would be. this
('gesture representation:', 'eNq1WMtSHEcQvM+PiIs36t3dP4CujuADHFhsACEZNmBlW3/vnOrZxyCWmYPFQQu5OdlVldXVPbp6/Pr494/N/fZ1//1lO3yePnc0XN3teLj59HT71/bTsBP8ig8dXm8+ve5fnr9uX/GnDVffdj5cvStyk7RhF6NUwfO758en/fhYHR9rFx77fWQNO+4RjCH8wCMsw/VvtCFhZWuspJXZfQzn3/FrHa5pE6WIca0NH00agv3z9uNFLBfx4f6i/v0krRKspZE7PnyFdKbNZYU0mykjZo3mXlZI15Ruy9Lu4ZWKSiFi9bIoLVl14RXSHI3xHQuLqyxHLZLS+iuk00ZZYaMFmqOYYAEn5hUFSRtlhY0mptZQ6mJsXpeV00Vp/7+ypom6wkQ0dEGRpXqgtW250pom6goT1biQRWth0Ddflk4T9cxErkSNpTRG3vVcm4TUpVR2GMoUy+Jpo57ZyFZI1aCOddjbSV0CO9FRkohCpdQVoaeV2n6NuqWddmYntWIW4UwmCvtO4oLODlPn0qyYy7J4GmpnhhKer7W0VkngXZSjOjdz9EtwiBf3FZ1o6amdeYrKQJdEGmae1ob9dZQPTEGBtw0UjMIV8umqTa6mehXEJVVauHLBpDyqq1NDsZpiZUeSy+rpqrWTulqphuHfEGr4eL4cxJmbSyiaEt5ili+Ke5rqfBLHucKKbega3BqdxOlNx7Rl8TTV9SQeCFwCa3hg59ip6KRNMGSDYLhSkxWRp6fua8Q5qFoNDC/sVCq8LJ6OelkU76MHuhHaGrQxJJa73dNSb6vkrWrBNrJWYSm226J6pKdx8pSxU4nMEaIX8jPtWsmKRuAfzMm2bGmkpXGyFIeXoZmrEKaukcpJnXGhaAptw4hwXZ4wkZaGrxJHw1QMN6/NcBtaUZW0NMoqcVyCzDB9WA0jhpYdjXQ02nLNhfAnYEyBEAzmRemSdhZe0yzYobhnRcMSaEpaHi4l/Sy6uP9HcVw+MXQKegYt4ysiTz+LL8/FUb1h7uJ72KoYYHkHGC/5X16226fjlb3EeGcvZbi6xiStGxqug6E87HelDreJ2gxtHTU5Ryt1VGYKlTtKM4UqiSLoGaodtbmCdZTnXE+UY64bHaWYoSVR8rlC5oazcAZmam3ObJkZfNu085+RYJ2QSWKM96dqBzPHSl1fJmamiNteBydmZoiZkaBOzEywUI9EwTz74ZGQucYkZVOomWpYlzJ+EzEImXUP/H1CVsDrpNCDwc5LdOqOI5p54x4/RzNx5zdoZo53tzmaqeNSMEczd3OfR2fDlNlIyeztsHSd0EzfpqWdfioaU+ZvRJcZWQCtlzU4i6HlsgZnYXRqiHcZ0hkfaGTB1D5gZPFUP2BkIVXmVeKsnR564IBm7XTaOkc06yXTLjmiWSOpc1SoozRHsxYyVevgn2T+uDHP0cxZfN4kknmKzVtSvKPz1pHMTdTmaOnom3Yv55SeqLQPKD1rKe9Qft5ImHmd7ivpvU7joDj/0Uv0XkChlfReWa4r6b3kl8cEzoTOoMuMbsW01aYBxdqH8WEOHNB+0Eyt8860w90kGSUuMqwfQNOJMI1RvF8m6jFH+wE0bb8j2g+g6fw5oqhFPzgfto/3D/vx/6Tw3nNtbzYctiM4/zze7R+SEsN4ao0rAN4/f9u+3D592eZXJd8sRnw65f/YvTzfff/StetwrRu8huCVAFT0PS484+V98x/WYONd')
('cross:', -1.2199187170964643)
('check:', -2.052277818300959)
('circle:', 0.4973932910874005)
('square:', 0.2907537534396739)
That's it, you have your string :D
I have another method that can be used. It is not through Carousel but it allows you to change screen through swiping. You can create a method that takes in the x point of your initial contact point of the screen and the x point of your final contact of the screen and subtract them. Here is the link to the video(https://www.youtube.com/watch?v=8pqtMAUEUyo&t=65s)
def on_touch_move(self, touch):
if touch.ox - touch.x > 50: # check for swiping gestures
# ScreenManger change screen
touch.ox is the initial contact point of the screen(x axis) and touch.x is the final. If the difference is then greater than a set value it changes screen.
As you asked in your comment.
I have bunch of pages. Each page will have a bunch of buttons. Some
pages have more buttons than can fit on the screen, so they need to be
scrollable.
Now, Scrollable part, you already figured out.(You can do it in kivy file also), See here. And you can easily add that in the code below.
Clicking on a button should take you to the next child
screen (with a scroll effect). On any child it should be possible to
go back to it's parent by swiping back.
Here(Code below) you can both swipe or click on buttons to navigate.
Now,
Given I am going to have a lot pages, I probably need the Carousel to
be loaded dynamically, using add_widget() and remove_widget().
These examples will help you.
Kivy-Showcase and Container
In kivy-showcase have a look at load_screen method and also the build function
Here is an example to add_widgets on click of a button
Builder.load_string('''
[SideBar#BoxLayout]:
content: content
orientation: 'vertical'
size_hint: .2,1
BoxLayout:
orientation: 'vertical'
# just add a id that can be accessed later on
id: content
<Root>:
Button:
center_x: root.center_x
text: 'press to add_widgets'
size_hint: .2, .2
on_press:
sb.content.clear_widgets()
root.load_content(sb.content)
SideBar:
id: sb
''')
class Root(BoxLayout):
def load_content(self, content):
for but in range(20):
content.add_widget(Button(text=str(but)))
class MyApp(App):
def build(self):
return Root()
if __name__ == '__main__':
MyApp().run()
Here is the example for screens.
Here is main.py file
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class ShowTime(BoxLayout):
carousel = ObjectProperty(None)
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class MainApp(App):
def build(self):
return ShowTime()
if __name__ == '__main__':
MainApp().run()
Here is the main.kv file
<Screen1>:
name: "screen1"
BoxLayout:
orientation: 'vertical'
padding: 50
spacing: 50
Button:
text: "Next (2)"
on_release: self.parent.parent.parent.parent.parent.ids.carousel.load_next()
Button:
text: "Go back (2)"
on_release: self.parent.parent.parent.parent.parent.ids.carousel.load_previous()
<Screen2>:
name: "screen2"
BoxLayout:
orientation: 'vertical'
padding: 100
spacing: 100
Button:
text: "go back (3)"
on_release: self.parent.parent.parent.parent.parent.ids.carousel.load_previous()
<Showtime>:
carousel: carousel
Carousel:
id: carousel
loop: True
BoxLayout:
padding: 100
spacing: 100
Button:
text: 'Tap me or Swipe (1)'
on_release: carousel.load_next()
Screen1:
Screen2:
EDIT 1:
Q- How to use load_slide() method?
load_slide() method takes slides as its parameter def load_slide(self, slide):
Q- So now how to get slide?.
slide is a list property slides = ListProperty([]),
Print this where button has text "go back (3)"
on_release: print( self.parent.parent.parent.parent.parent.ids.carousel.slides)
you will get a list of all slides under id(carousel).
This is how you use it.
.....ids.carousel.load_slide(....ids.carousel..slides[2])

Resources