Kivy change slider design - python

Is it possible to make a kivy slider look like this one?
This is my python code:
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.core.window import Window
class MyWidget(GridLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
Window.clearcolor = (1, 1, 1, 1)
class PhoneApp(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
PhoneApp().run()
This is my kv code:
#:kivy 2.1.0
<Label>
size_hint_y: None
height: self.texture_size[1]
color: 0, 0, 0, 1
<Image>
size_hint_y: None
height: self.texture_size[1]
<Button>
size_hint_x: None
size_hint_y: None
width: self.texture_size[0]
height: self.texture_size[1]
<Slider>
size_hint_y: None
<GridLayout>
cols: 1
size_hint: (0.5, 0.9)
pos_hint: {"center_x": 0.5, "center_y": 0.5}
<MyWidget>:
Image:
source: "captus.png"
Label:
text: "Test Message"
Button:
text: "Second test"
Slider:
id: slider
min: 0
max: 100
For now the slider looks like this:
Is it possible to make it look like the first one?
Also.. I'm wrong or it's like bugged, not aligned with the bar?

First of all, you generally should not create dynamic classes with their default class names as it may affect its overall design for all of its instances which you may not want. Rather create an instance of that class and apply your design or logic therein. So you should change the lines like <Button> in kvlang with <MyButton#Button>.
Now since here you're using the GridLayout as a container you have fewer control over its children's attributes like pos etc. The Slider is also a widget, so you can adjust its size. To change its appearance you can use its properties like cursor_image etc. With all these changes your code in kvlang should now look something like the following,
#:kivy 2.1.0
<MyLabel#Label>
size_hint_y: None
height: self.texture_size[1]
color: 0, 0, 0, 1
<MyImage#Image>
size_hint_y: None
height: self.texture_size[1]
<MyButton#Button>
# size_hint_x: None
size_hint_y: None
# width: self.texture_size[0]
height: self.texture_size[1]
<MySlider#Slider>
size_hint_y: None
height: dp(64) # Set specific height.
cursor_image: "path to image"
background_horizontal: "some path to image"
#<MyGridLayout#GridLayout>
# cols: 1
# size_hint: (0.5, 0.9)
# pos_hint: {"center_x": 0.5, "center_y": 0.5}
<MyWidget>:
cols: 1
size_hint: (0.5, 0.9)
pos_hint: {"center_x": 0.5, "center_y": 0.5}
MyImage:
keep_ratio: True
source: "captus.png"
MyLabel:
text: "Test Message"
MyButton:
text: "Second test"
MySlider:
id: slider
min: 0
max: 100

Related

How to recalculate texture_size in Kivy after condition reached

I'm trying to optimize the chat bubble mechanism, but still can't figure out the values that I need to set for my texture_size.
As you can see in the picture below, the first chat bubble seems to be okay, but when the second one has more words and reach the width limit then down the line, the label's texture_size[0] will be replaced as its max-width (the orange one) which means the box containing the label (the green one) that has its size attach to the label's texture_size will have its width set to the max-width and won't be able to track the label's texture_size anymore and cause some gaps as I showed below (the red one).
The gaps should be removed like this:
Here's a small app that I convert from the issue code.
.py file
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import ListProperty,BooleanProperty, StringProperty
from datetime import datetime
class HistoryBox(BoxLayout):
chat_logs = ListProperty()
def send(self, text):
now = datetime.now()
current_time = now.strftime("%H:%M")
self.create_chatbubble(text,True,current_time)
self.ids.field.text = ""
def create_chatbubble(self,message,send_by_user,time):
self.chat_logs.append(
{
"text":message,
"send_by_user": send_by_user,
"time": time,
}
)
class ChatBubble(BoxLayout):
send_by_user = BooleanProperty()
text = StringProperty()
time = StringProperty()
class App(App):
def build(self):
self.root = Builder.load_file("my.kv")
if __name__ == "__main__":
App().run()
.kv file
HistoryBox:
<HistoryBox>:
orientation: "vertical"
RecycleView:
data: root.chat_logs
viewclass: "ChatBubble"
RecycleBoxLayout:
id: box
padding: dp(10)
spacing: dp(15)
orientation: "vertical"
height: self.minimum_size[1]
default_size_hint: 1, None
default_size: None, None
TextInput:
id: field
hint_text: "Write your message"
multiline: False
focus: True
padding: dp(7)
size_hint_y: .6
size_hint_max_y: (len(self._lines)+1)*self.line_height
on_text_validate:
root.send(self.text)
<ChatBubble>:
id: chtbld
md_bg_color: [0, 0, 0, 0]
size_hint_y: None
height: wrapped.height
pos_hint: {'right': 1} if chtbld.send_by_user == True else {'x': 0}
#adaptive_height: True
width: root.width
padding: [10, 0, 10, 0]
orientation: 'vertical'
BoxLayout:
id: wrapped
height: msg_content.height + timed.height + 10
width: msg_content.width + wid1.width + wid2.width
size_hint: None, None
pos_hint: {'right': 1} if chtbld.send_by_user == True else {'x': 0}
canvas.before:
Color:
rgba: 1,1,1,1
RoundedRectangle:
pos: self.pos
size: self.size
radius: [10, 10, (1, -5), 10] if self.pos_hint == {'right': 1} else [10, 10, 10, (1, -5)]
Spacer:
id: wid1
BoxLayout:
orientation: 'vertical'
height: msg_content.height + tc.height + wid3.height
width: msg_content.width
Label:
id: msg_content
text: root.text
width: tc.width if self.texture_size[0] < tc.width else self.texture_size[0]
height: self.texture_size[1]
size_hint_y: None
text_size: chtbld.width-dp(100) if self.width >= chtbld.width-dp(100) else None,None
halign: 'left'
color: 0, 0, 0, 1
BoxLayout:
id: tc
size_hint: None, None
height: timed.height
width: timed.width + 3
pos_hint: {'right': 1}
spacing: 3
Label:
id: timed
text: root.time
size_hint: None, None
size: self.texture_size
font_size: 9
bold: True
text_size: None,None
color: [.8, .8, .8, 1]
Spacer:
id: wid3
height: 5
Spacer:
id: wid2
<Spacer#Widget>:
id: wid
width: 5
size_hint: None, None
Just type anything like "welcomeeeeee toooooooooooooo" then minimize the window till the sentence down the line and you will notice the problem. Any help or advice would be helpful.

Adaptive_width property of MDLabel is not working correctly

I am making an app using kivy & kivymd and in one part of it, I would like the labels to take as much space as the actual text.
This seems pretty straightforward with kivy itself but for some reason, nothing works with the MDLabel class. I tried setting the adaptive_width property to True and I also tried to directly set the width to the texture_size[0] property but none of them worked (and yes I installed kivymd directly from github).
Here is my code:
from kivy.lang import Builder
from kivymd.app import MDApp
class MainApp(MDApp):
def __init__(self, **kwargs):
super(MainApp, self).__init__(**kwargs)
self.kv = Builder.load_string('''
#:kivy 2.0.0
BoxLayout:
MDLabel:
text: "Supposedly adaptive width (KivyMD)"
font_size: "21sp"
halign: "center"
adaptive_width: True
# I also tried directly setting the width to the texture_size but the results were worse
# size_hint_x: None
# width: self.texture_size[0]
canvas.before:
Color:
rgba: .8, .1, .2, .5
Rectangle:
pos: self.pos
size: self.size
Widget:
MDSeparator:
orientation: "vertical"
Widget:
Label:
text: "Actual adaptive width (Standard Kivy)"
font_size: "21sp"
color: 0, 0, 0, 1
size_hint_x: None
width: self.texture_size[0]
canvas.before:
Color:
rgba: 0, .6, .2, .5
Rectangle:
pos: self.pos
size: self.size
''')
def build(self):
return self.kv
if __name__ == '__main__':
MainApp().run()
Here is my results:
I don't believe that MDLabel supports the adaptive_width property. In using the width: self.texture_size[0], it seems that you must also add the text_size: None, None to the MDLabel, and it seems that its location in the kv is important. Here is a version of part of your kv that seems to work:
BoxLayout:
MDLabel:
text: "Supposedly adaptive width (KivyMD)"
font_size: "21sp"
halign: "center"
# adaptive_width: True
# I also tried directly setting the width to the texture_size but the results were worse
size_hint_x: None
width: self.texture_size[0]
text_size: None, None # added, and must be in this location
canvas.before:
Color:
rgba: .8, .1, .2, .5
Rectangle:
pos: self.pos
size: self.size

Displaying different widget types in Recycleview Kivy

I was wondering if it possible to display different widget types in kivy recycleview, I have it displaying textboxes which is great but would it be possible to display Images in the same recycle view or do I have to create another recycleview on top on my existing one, this is my code and current app snapshot, my current understanding is that you can only have one viewclass in recycle view mine is a text input so how would one add another view class to the same recyclerview.
current screen
https://i.stack.imgur.com/u8JpE.jpg
main.kv
SelectableReportTextbox:
size_hint: None,None
text_size : self.text_size
size_hint_y: None
font_size: self.height*0.2
foreground_color: [1, 1, 1, 1]
readonly: True
background_color: (0.1, 0.3, 0.7, 0.7)
padding_x: [30,30]
font_name: 'C:\kivy_venv\Graphics\GIL_____.TTF'
border: [50,50,50,50]
ScreenTwo:
canvas.before:
Rectangle:
size:self.size #100, 100
pos: self.pos
source: "C:\kivy_venv\Graphics\Jetfire back.png"
RecycleView:
do_scroll: True, True
bar_width: 6
size_hint: (None, None)
id: scrlv
size: (500, 500)
pos_hint: {'center_x': .75, 'center_y': .64}
scroll_y: 0
multiline:True
ProjectRV:
viewclass: 'SelectableReportTextbox' # defines the viewtype for the data items.
orientation: "vertical"
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
key_size: "height"
padding:1, 1
space_x: self.size[0]/3
id: rv
pos_hint: {'center_x': 0.32, 'center_y': 0.525}
bar_width: dp(25)
bar_color: (0.7, 0.1, 0.3, 0.7)
bar_inactive_color: (0.1, 0.1, 0.1 , 1)
scroll_y : 0
SelectableRecycleBoxLayout:
data : []
spacing : '5'
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
multiselect: True
touch_multiselect: True
orientation: 'vertical'
SelectableRecycleBoxLayout:
data : []
color:(0, 0.7, 0.4, 0.4)
spacing : '5'
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
multiselect: True
touch_multiselect: True
orientation: 'vertical'
You can define your own viewclass that displays whatever you want. Your viewclass must have Properties that correspond to the keys in the data of the RecycleView.
Here is a simple example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
class RV(RecycleView):
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
# set up the data
self.data = [
{'text': 'Some Text', 'image': 'dots.png'},
{'text': 'more text', 'image': 'tester.png'}
]
kv = '''
<MyViewClass#BoxLayout>:
# define the properties that appear in the data
text: ''
image: ''
# define how the data is displayed
Image:
source: root.image
Label:
text: root.text
RV:
viewclass: 'MyViewClass'
RecycleBoxLayout:
padding: 10, 0, 10, 0
size_hint_y: None
height: self.minimum_height
default_size: None, 40
default_size_hint: 1, None
orientation: 'vertical'
spacing: 3
'''
class TestApp(App):
def build(self):
return Builder.load_string(kv)
TestApp().run()

Why is Screens not loading in Kv Language?

I have an App with three toggle buttons in a fixed header that is an outside indented layout for screen manager. On initialisation, the Import screen must show i.e. self.ids.scrn_man.current = 'import_scn" and when a toggle button is pushed a next screen should show i.e. on_state: scrn_man.current = "settings_scrn".
But for some reason, only the header is showing and the screens do not want to transition. I don't get any errors.
I tried different layouts as my Apps main class inheritance including, FloatLayout, StackLayout and BoxLayout. I also made the header fixed with AnchorLayout and use a different layout as the inline layout for ScreenManager. If I remove the ScreenManager I see my widgets, but of course, I cannot transition. I did at first try using TabbedPanel to house my different widgets but I encountered a constant RefError: weak object reference if I added too many widgets (but that's not for now). So I re-designed with something I knew worked on a previous App albeit less complex.
Here is my faulty code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.stacklayout import StackLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.properties import StringProperty, ObjectProperty
Builder.load_string("""
<RoundedButton#Button>:
background_color: 0,0,0,0
canvas.before:
Color:
rgba: (.47,.47,.47,1) if self.state=='normal' else (1,.6,0,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [8,]
<RoundedCancelButton#Button>:
background_color: 0,0,0,0
canvas.before:
Color:
rgba: (.47,.47,.47,1) if self.state=='normal' else (1,.2,.2,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [8,]
<RoundedAcceptButton#Button>:
background_color: 0,0,0,0
canvas.before:
Color:
rgba: (.47,.47,.47,1) if self.state=='normal' else (.2,1,.6,1)
RoundedRectangle:
pos: self.pos
size: self.size
radius: [8,]
<TabbedContainer#ToggleButton>:
background_color: (1, .5, 0, 1)
background_normal: ''
size_hint_y: None
height: 50
size_hint_x: (1 / 3)
spacing: 30
<Tab>:
canvas.before:
Color:
rgba: (.89, .89, .89, 1)
Rectangle:
size: self.size
pos: self.pos
orientation: 'lr-tb'
BoxLayout:
orientation: 'horizontal'
size_hint_y: None
height: 30
canvas.before:
Color:
rgba: (1, .3, 0, 1)
Rectangle:
size: self.size
pos: self.pos
Label:
text: 'Test'
color: (1, 1, 1, 1)
size_hint_x: 1
StackLayout:
orientation: 'lr-tb'
Label:
text: ''
size_hint_x: 1
size_hint_y: None
height: 10
TabbedContainer:
id: import_tog
text: 'Import'
state: 'down'
group: 'admin_navs'
on_state: root.change_screen(self)
TabbedContainer:
id: calculate_tog
text: 'Calculate'
group: 'admin_navs'
on_state: root.change_screen(self)
TabbedContainer:
id: settings_tog
text: 'Settings'
group: 'admin_navs'
on_state: root.change_screen(self)
BoxLayout:
id: ui_content
padding: 10
ScreenManager: #Problem here I think
id: scrn_man
Screen:
id: import_scrn
name: 'import_scrn'
StackLayout:
orientation: 'lr-tb'
Label:
text: ''
size_hint_x: 1
Label:
text: ''
size_hint_x: 0.2
RoundedButton:
text: 'Choose File'
size_hint_x: 0.2
TextInput:
id: get_file
readonly: True
size_hint_x: 0.5
Label:
text: ''
size_hint_x: 0.1
Label:
text: ''
size_hint_x: 0.2
RoundedButton:
text: 'Import'
size_hint_x: 0.2
Label:
text: ''
size_hint_x: 0.6
StackLayout:
id: import_data_content
orientation: 'lr-tb'
size_hint_y: None
height: 90
Screen:
id: calculate_scrn
name: 'calculate_scrn'
Screen:
id: settings_scrn
name: 'settings_scrn'
StackLayout:
orientation: 'lr-tb'
size_hint_x: 0.5
Label:
text: ''
size_hint_x: 0.1
Button:
text: 'Add Employee'
size_hint_x: 0.2
Label:
text: ''
size_hint_x: 0.2
Button:
text: 'CSV'
size_hint_x: 0.2
Label:
text: ''
size_hint_x: 0.3
BoxLayout:
orientation: 'horizontal'
size_hint_x: 0.5
Label:
text: 'In Time'
size_hint_x: 0.7
TextInput:
size_hint_x: 0.3
Label:
text: 'Out Time'
size_hint_x: 0.7
TextInput:
size_hint_x: 0.3
""")
class TabbedContainer(ToggleButton):
pass
class FileChoosePopup(Popup):
load = ObjectProperty()
class RoundedButton(Button):
pass
class RoundedCancelButton(Button):
pass
class RoundedAcceptButton(Button):
pass
class Tab(StackLayout):
file_path = StringProperty("No file chosen")
the_popup = ObjectProperty(None)
def __init__(self, **kwargs):
super().__init__(**kwargs)
#load import window on initialisation
import_window = self.ids.import_scrn
self.ids.scrn_man.current = 'import_scrn'
def change_screen(self, instance):
if instance.text == 'Import':
self.ids.scrn_man.current = 'import_scrn'
elif instance.text == 'Calculate':
self.ids.scrn_man.current = 'calculate_scrn'
else:
self.ids.scrn_man.current = 'settings_scrn'
class TestApp(App):
def build(self):
return Tab()
if __name__ == '__main__':
TestApp().run()
I expect that the import screen must show on initialisation and screens transition on toggle button state: down. Can someone please give me some advice on how to make my App act as explained above?
Your screens are loading correctly according to your settings. You need to review your entire kv string looking at your size_hint settings. Check each item that contains children and make sure that the total of size_hint_x for its children is less than or equal to 1.0 and the same for size_hint_y.

How do I position widgets and use layouts in Kivy?

This: (https://imgur.com/a/Y9Xwl) (can't format it for some reason) is the user interface I am currently trying to create in Kivy. I am having difficulty recreating this as I do not understand the layout system and I have read a lot of documentation, watched a lot of Youtube videos, tinkered with the code and still I cannot get the desired result. So far this is my code, it has all the widgets that I need within it, they're just not sized/positioned how I want them:
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang import Builder
from kivy.properties import ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
class CaloriesScreen(Screen):
pass
class categoriesScreen(Screen):
pass
class loginScreen(Screen):
pass
class registerScreen(Screen):
pass
class shoppingListScreen(Screen):
pass
class theScreenManager(ScreenManager):
pass
root_widget = Builder.load_string('''
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
theScreenManager:
transition: FadeTransition()
CaloriesScreen:
<CaloriesScreen>:
name: 'calories'
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
size_hint: 1, .3
Button:
text: '<'
size_hint: .1, 1
font_size: 75
background_normal: ""
background_color: 0.18, .5, .92, 1
on_release: app.root.current = 'main'
Label:
text: 'Calories'
halign: 'left'
font_size: 50
canvas.before:
Color:
rgb: 0.18, .5, .92
Rectangle:
pos: self.pos
size: self.size
Widget:
size_hint: .1, 1
canvas.before:
Color:
rgb: 0.18, .5, .92
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'horizontal'
size_hint: 1, .4
canvas.before:
Color:
rgb: 0.8, 0.8, 0.8
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'Recipes'
font_size: 30
color: 0.18, .5, .92, 1
size_hint: 1, 1
Button:
id: btn
text: 'Select a recipe...'
on_release: dropdown.open(self)
height: '48dp'
size_hint: .5, .3
pos: self.x, .3
DropDown:
id: dropdown
on_parent: self.dismiss()
on_select: btn.text = '{}'.format(args[1])
Button:
text: 'First recipe'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('First Item')
Button:
text: 'Second recipe'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('Second Item')
Button:
text: 'Third recipe'
size_hint_y: None
height: '48dp'
on_release: dropdown.select('Third Item')
Button:
text: '+'
font_size: 30
background_normal: ""
background_color: 0.18, .5, .92, 1
#on_release:
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Food'
font_size: 30
color: 0.18, .5, .92, 1
Label:
text: 'Cal'
font_size: 30
color: 0.18, .5, .92, 1
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Simple Cheese Omelette'
font_size: 30
color: 0.18, .5, .92, 1
Label:
text: '241'
font_size: 30
color: 0.18, .5, .92, 1
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Burger'
font_size: 30
color: 0.18, .5, .92, 1
Label:
text: '295'
font_size: 30
color: 0.18, .5, .92, 1
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Tomato and caper linguine '
font_size: 30
color: 0.18, .5, .92, 1
Label:
text: '393'
font_size: 30
color: 0.18, .5, .92, 1
BoxLayout:
orientation: 'vertical'
canvas.before:
Color:
rgb: 0.8, 0.8, 0.8
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
orientation: 'horizontal'
Label:
text: 'Total Cal'
font_size: 30
color: 0.18, .5, .92, 1
Label:
text: '929'
font_size: 30
color: 0.18, .5, .92, 1
BoxLayout:
orientation: 'horizontal'
Label:
text: 'You are under calories'
font_size: 30
color: 0.18, .5, .92, 1
Button:
text: 'Clear'
font_size: 75
background_normal: ""
background_color: 0.18, .5, .92, 1
#on_release:
''')
class RecipeApp(App):
def build(self):
return root_widget
if __name__ == "__main__":
RecipeApp().run()
This(https://imgur.com/a/zW2z0) (can't format it for some reason) is what the output of that code looks like. The top bar with the "<" button is how I want it and I've only tried to edit the horizontal row of widgets below it. I cannot position the dropdown menu with the 'select your recipe' label how I would like it. I have tried altering it's y-axis multiple times but it always sinks to the botton of the boxlayout. I even tried giving it a new boxlayout just for the dropdown and tried doing: pos: self.parent.x, self.parent.y * 0.5 assuming it would go halfway up the y-axis of it's parent layout (the boxlayout) but still nothing. I am wondering whether it would be better to just use a floatlayout and manually position all the widgets but I am unsure how this will work well when I compile it into an APK for an Android device. What is the best way to go about positioning my widgets on the screen?
Use pos_hint for this.
If pos_hint: {'top': 1}, the top of the widget will hit the roof of the parent box.
So if your widgets height is 30% of its parent box (size_hint: 0.5, 0.3), and you want it to be centered vertically, you want pos_hint: {'top': 0.5 + 0.3/2}, which means the top of the widget will be half way to the roof + half of the widgets height which is 15% of the parent box.
That takes us 65% to the top :)
size_hint: 0.5, 0.3
pos_hint: { 'top' : 0.65}
If the widgets size_hint is dynamic you can do something like this.
pos_hint: {'top': 0.5 + self.size_hint[1]/2}
And lets take your Select recipe button as an example:
Button:
id: btn
text: 'Select a recipe...'
on_release: dropdown.open(self)
height: '48dp'
size_hint: .5, .3
pos_hint: {'top': 0.5 + self.size_hint[1]/2}

Categories

Resources