Problem with Kivy - Scrollview overlapping with Box Layout above it - python

I'm clicking the button in the fixed menu at the top even though there is a Button in the vertical Scrollview, it doesn't effect the fixed menu.
I'm trying to click at the button in the fixed menu at the top but the actions are being sent to the horizontal Scrollview below.
I'm new with kivy and even programming and I'm creating an app with Python 3.7, and Kivy(1.11.1) that kinda looks like Netflix's Page layout, in which there is a menu fixed at the top and below it there is a vertical Scrollview with some horizontal scrollviews inside (just like the Netlix's catalogues, eg.: My List, Continue Watching, Series, etc). The problem is that when I scroll down the vertical ScrollView and one of the horizontal scrollview gets behind the fixed menu at the top, the events are sent to this horizontal scrollview and not to the fixed menu that is be above the Scrollview content. If I scroll the vertical Scrollview down and one of the horizontal scrollviews gets "behind" (in quotes because I imagine they aren't supposed to collide with each other) the menu at the top, if I click in the menu the event is being sent to the horizontal scrollview.
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.scrollview import ScrollView
Builder.load_string(
'''
<ScrollApp>:
orientation: 'vertical'
BoxLayout:
size_hint: 1, 0.12
Button:
text: 'Fixed Menu'
on_press: print('This button stops working if there is a horizontal scrollview "behind"')
ScrollView:
bar_width: 10
scroll_type: ['bars', 'content']
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
padding: 22, 0, 22, 50
spacing: 50
canvas:
Color:
rgba: .15, .15, .15, .9
Rectangle:
size: self.size
pos: self.pos
Button:
size_hint: None, None
width: 100
height: 100
on_press: print('This button does not overlap the menu above')
# "ScrollViews containers"
Custom
Custom
Custom
Custom
Custom
BoxLayout:
size_hint: 1, 0.12
Button:
on_press: print("This menu at the bottom is not affected by the problem that occurs with the top one")
<Custom#BoxLayout>:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
Label:
size_hint: None, None
size: self.texture_size
id: label
font_size: 20
text: 'Teste'
ScrollView:
do_scroll: True, True
size_hint: 1, None
height: 150
GridLayout:
id: grid
size_hint: None, 1.01
width: self.minimum_width
spacing: 5
cols: 3
Button:
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
''' )
class ScrollApp(BoxLayout):
pass
class Test(App):
def build(self):
return ScrollApp()
Test().run()

You do manage to find obscure situations in kivy :-).
I believe the problem here is the order of touch event processing. The touch events are normally processed by the top level widgets, and they dispatch the event to their children, all the way down the Widget tree. In any Widget with multiple children, the touch event is dispatched to children in the reverse order that they have been add to their parent. And in kv, child Widgets are added in order as they appear (top to bottom). So, in your case, the ScrollApp has three children, and the touch events will be dispatched to its children starting with the BoxLayout that contains the bottom menu, then to the ScrollView, and lastly, to the BoxLayout that contains the top menu. So the ScrollView gets the touch event before the top menu, and dispatches the touch to its children (including the Custom instances). So the Buttons in the Custom see the touch event before the top menu sees it, and they claim it as their own. The obvious way to fix it would be to change the order of Widgets that appear in the ScrollApp, but since ScrollApp is a BoxLayout, that would dramatically change how your App looks. A different fix is to use a Layout that doesn't depend on the order of its children. Something like a FloatLayout has that characteristic. So, here is a modified version of your code, that makes ScrollApp extend FloatLayout instead of BoxLayout:
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string(
'''
<ScrollApp>:
ScrollView:
size_hint: 1.0, 0.76
pos_hint: {'center_y':0.5}
bar_width: 10
scroll_type: ['bars', 'content']
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
padding: 22, 0, 22, 50
spacing: 50
canvas:
Color:
rgba: .15, .15, .15, .9
Rectangle:
size: self.size
pos: self.pos
Button:
size_hint: None, None
width: 100
height: 100
on_press: print('This button does not overlap the menu above')
# "ScrollViews containers"
Custom
Custom
Custom
Custom
Custom
BoxLayout:
size_hint: 1, 0.12
pos_hint: {'y':0}
Button:
on_press: print("This menu at the bottom is not affected by the problem that occurs with the top one")
BoxLayout:
size_hint: 1, 0.12
pos_hint: {'top':1.0}
Button:
text: 'Fixed Menu'
on_press: print('This button stops working if there is a horizontal scrollview "behind"')
<Custom#BoxLayout>:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
Label:
size_hint: None, None
size: self.texture_size
id: label
font_size: 20
text: 'Teste'
ScrollView:
do_scroll: True, True
size_hint: 1, None
height: 150
GridLayout:
id: grid
size_hint: None, 1.01
width: self.minimum_width
spacing: 5
cols: 3
Button:
text: 'button 1'
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
text: 'button 2'
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
Button:
text: 'button 3'
size_hint: None, None
size: 400, 150
on_press: print('ScrollView button pressed')
''' )
class ScrollApp(FloatLayout):
pass
class Test(App):
def build(self):
return ScrollApp()
Test().run()
So, with this child order in ScrollApp, the top and bottom menus get their shot at the touch events before the ScrollView.
I actually think your code should work without this change, so there may be something else happening that I couldn't find.

Related

How to change a Label text on another screen by pressing a Button Python Kivy

How can I change the value of text in Label on the 2nd screen by pressing a Button on the 1st screen?
In my example, I have 2 screens, on the first there are 3 buttons; one should change the text to "1st text", second should change the text to "2nd text" and the third is used to move between these two screens.
On the second screen, there is a Label which text should be changed by pressing the buttons. Then, there is also the button used to move to the first screen.
My .py looks like:
import kivy
kivy.require("1.10.1")
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.app import App
class Screen1(Screen):
pass
class Screen2(Screen):
pass
class Select_text(App):
def build(self):
sm = ScreenManager(transition=FadeTransition())
sm.add_widget(Screen1(name = "scr1"))
sm.add_widget(Screen2(name = "scr2"))
return sm
app = Select_text()
app.run()
My .kv seems like:
<Screen1>:
id: scr1
orientation: "vertical"
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: "Background.png"
Button:
id: change_to_1
pos: (root.width-self.width)/2, 400
size: 1200, 200
size_hint: None, None
text: "Change the text on the 2nd screen to »1st text«"
#on_press: (I don‘t know what should be there)
Button:
id: change_to_2
pos: (root.width-self.width)/2, 800
size: 1200, 200
size_hint: None, None
text: "Change the text on the 2nd screen to »2nd text«"
#on_press: (I don‘t know what should be there)
Button:
id: go_to_other_screen
pos: (root.width-self.width)/2, 1400
size: 600, 200
size_hint: None, None
text: "Go to other screen"
on_press: root.manager.current = "scr2"
<Screen2>:
id: scr2
orientation: "vertical"
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: "Background.png"
Label:
id: text
text: "Text which should be changed"
pos: (root.width-self.width)/2, 800
size: 600, 200
Button:
id: go_to_other_screen
pos: (root.width-self.width)/2, 1400
size: 600, 200
size_hint: None, None
text: "Go to other screen"
on_press: root.manager.current = "scr1"
I tried to search on the internet, but it didn't solve the main issue.
Thanks for any answer.
In the kv file call a function in the screen 1 class. You can then use the get_screen function to access the other screen and change its text in that function.
Would probably look something like:
(in the kv file)
on_press: root.functionname()
(main python file)
def functionname(self):
self.manager.get_screen('scr2').ids.text.text = "whatever you want here"
May i also suggest changing the id of the text ur changing to something else, because it looks a bit confusing with ids.text.text
For more info about the get_screen function if you are confused about that https://medium.com/nerd-for-tech/kivy-use-get-screen-to-access-objects-from-other-screens-8d4d6f288f3

I can't position my buttons in scrollview in kivy

Im creating a simple chat room and i want it to look like whatsapp and messager so I add ScrollView and inside the ScrollView I make a GridLayout class to be containers of upcoming message but when I add AnchorLayout to position the Button inside the GridLayout my widget are colliding and its get messy
See the 2 button that colliding
Here are my code in that part:
ScrollView:
size_hint: 1 , 0.8
scroll_distance: 0
BoxLayout:
id: messages
cols: 1
size_hint_y: None
height:self.minimum_height
spacing: dp(10)
orientation: "vertical"
# Display Date
Button:
text: root.now
align: "center"
size_hint: 1 , None
height: self.texture_size[1] + dp(15)
AnchorLayout:
anchor_x: "right"
size: test.size[0] , test.size[1] + dp(20)
Button:
id: test
text: root.now
align: "center"
size_hint: None , None
height: self.texture_size[1] + dp(15)
width: self.texture_size[0] + dp(15)

KivyMD MDRectangleFlatIconButton's text is in two lines when text is updated with a long text

I want to update MDRectangleFlatIconButton's text. It changes with a new text but when the new text is longer than previous text, then the text fits in two lines.
When i use a normal button, the new text fits in one line with proper adjustment.
but Since there is a Icon in MDRectangleFlatIconButton, when a new text is longer than previous text, the text fits in a two line.
To run the program,
Add a new app name which is longer than "Info" inside "Info" button's popup window, then click "Update Top Bar's name". Then, it updated title and the text of "Info" button at the front main App.
I have tried to change this by adding button's text_size: self.width, valign:"center", haling: "center", or manually adding text_size: cm(10), cm(10).
Also, i tried with on_release: "app.root.ids.bt_information.text_size = self.width, None
but nothing works so far.
I greatly appreciate your help.
Python code
'''
from kivy.uix.widget import Widget
'''Setting the size of first window for program'''
from kivy.config import Config #another way of setting size of window
Config.set('graphics', 'width', '600') # from kivy.core.window import Window
Config.set('graphics', 'height', '750') # Window.size = ("600", "750")
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
Builder.load_file('new_window_popup.kv')
class Dex(Popup):
pass
class Remi(Popup):
pass
class Info(Popup):
pass
class MyLayout(Widget):
pass
class AwesomeApp(MDApp):
def build(self):
self.title = "My house"
return MyLayout()
if __name__ == '__main__':
AwesomeApp().run()
'''
kivyfile: new_window_popup.kv
'''
#:import Factory kivy.factory.Factory
#:import MDRaisedButton kivymd.uix.button
<Dex>:
auto_dismiss: False
size_hint: 1, 1
title: "Weight-Based Dose Calculator "
canvas.before:
Color:
rgba: (0,1,0,1)
Rectangle:
pos:self.pos
size:self.size
BoxLayout:
orientation: "vertical"
size:root.width, root.height
Label:
text: "Dex 1"
Button:
text: "Close"
font_size: 24
on_release: root.dismiss()
<Remi>:
auto_dismiss: False
size_hint: 1, 1
title: "Weight-Based Dose Calculator "
canvas.before:
Color:
rgba: (0,1,0,1)
Rectangle:
pos:self.pos
size:self.size
BoxLayout:
orientation: "vertical"
size:root.width, root.height
Label:
text: "Remi"
Button:
text: "Close"
font_size: 24
on_release: root.dismiss()
<Info>:
appName:appName
auto_dismiss: False
size_hint: 1, 1
title: "Change Info"
canvas.before:
Color:
rgba: (0,1,0,1)
Rectangle:
pos:self.pos
size:self.size
BoxLayout:
orientation: "vertical"
size:root.width, root.height
Label:
text: "What is your App name?"
BoxLayout:
orientation: "horizontal"
MDTextField:
id: appName
hint_text: "App Name"
color_mode: 'primary'
current_hint_text_color: 1,1,1,1
hint_text_color_focus: 1,1,1,.9
line_color_focus: 1,1,1,1
font_size: '25sp'
text_color_normal: 1,1,1,.9
text_color_focus: 0,0,1,.9
focus: True
write_tab: False
Button:
text: "Update Top Bar\'s name"
font_size: 24
size_hint: .8, .2
# on_release: root.updateName()
on_release:
app.title = appName.text
app.root.ids.bt_information.text = appName.text
Button:
text: "Close"
font_size: 24
on_release: root.dismiss()
<MyLayout>
MDBoxLayout:
orientation:"vertical"
size: root.width, root.height
MDRaisedButton:
text: "Dex"
font_size: 32
text_color: 0,0,0,.9
size_hint: 1,.5
on_press: Factory.Dex().open()
MDRaisedButton:
text: "Remi"
font_size: 32
size_hint: 1,.5
on_press: Factory.Remi().open()
MDRectangleFlatIconButton:
id: bt_information
text: "Info"
icon: "youtube-studio"
font_size: 32
size_hint: 1,.2
text_size: self.width, None
md_bg_color: 0.95,0.61,0.73,1
on_press: Factory.Info().open()
'''

How to fix the position of a Kivy layout when the screen resolution changes as the program is open?

I have built a user interface with Kivy that appears correctly when the program starts up, but if I plug in a new monitor (or drag to an extended monitor), everything disappears under the window, but I can see part of my work when I maximize the window. How do I position my program to the start of the window?
I have tried to match the main layout top value to the window top value every second, and I have tried to reset the position of the main layout every second.
My kivy file
#:import inch kivy.metrics.inch
<Questionaire>:
#column_gl: ColumnGL
outer_column_gl: OuterColumnGL
cols: 2
GridLayout:
id: OuterColumnGL
cols: 1
size_hint_x: None
width: inch(2)
GridLayout:
id: ColumnGL
cols: 2
size_hint_y: .9
Button:
size_hint_x: None
width: inch(.1)
Button:
text: 'stuff'
Button:
size_hint_y: .1
text: 'more stuff'
My attempt to fix it
def fix_screen(*args):
self.y = 0
Clock.schedule_interval(fix_screen, .5)
This is what comes up when I first open the program.
This is what comes up as soon as I drag my window to an extended monitor (Everything appears to have disappeared).
Here is my maximized window on the extended monitor (The y position is wrong).
Using size_hint_x or size_hint_x allows you to set the size of your widgets relative to their parent widgets. Using ''pos_hint: {'x': val, 'y': val}'' will set the position of the widget to their parent widgets. Since size_hint, and pos_hint both are relative to their parent widgets, the values for the x and y are in a range from 0-1.
I would recommend utilizing size_hint_x: 0-1 rather than width for your buttons so they will automatically adjust to a changing resolution. I will come up with a sample to help you out.
Sizing and position are best done in the kv language whether you use builder or a .kv file. The added class and function are not needed to have reactive widgets. Let me know if you need more clarification or have any other questions about kivy.
Again I would recommend this following your widgets if you would like them to autoscale:
size_hint_x: 0.00-1.00
size_hint_y: 0.00-1.00
pos_hint: {'x': 0.00-1.00, 'y': 0.00-1.00}
An example that should show you how it works, although I doubt you need all the extra imports as this was to show consistency when changing resolutions:
from kivy.app import App
from kivy.lang import Builder
from kivy.config import Config
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
Config.set('input', 'mouse', 'mouse,multitouch_on_demand') #Prevent red dots from populating the screen with right clicks
Config.set('graphics','resizable',0) #Self-explanatory, prevents you from resizing the window
Config.write() #Writes the Config settings for the application
Builder.load_string("""
<One>:
GridLayout:
cols: 2
size_hint_x: .5
pos_hint: {'x': 0, 'y': 0}
GridLayout:
id: OuterColumnGL
cols: 1
size_hint_x: .2
GridLayout:
id: ColumnGL
cols: 2
size_hint_y: .9
Button:
size_hint_x: .3
Button:
text: 'stuff'
Button:
size_hint_y: .1
text: 'more stuff'
Button:
text: 'Change Res'
size_hint_x:.2
size_hint_y: .2
pos_hint: {'x': .75, 'y': .05}
on_release: root.enlarge()
<Two>:
GridLayout:
cols: 2
size_hint_x: .5
pos_hint: {'x': 0, 'y': 0}
GridLayout:
id: OuterColumnGL
cols: 1
size_hint_x: .2
GridLayout:
id: ColumnGL
cols: 2
size_hint_y: .9
Button:
size_hint_x: .3
Button:
text: 'stuff'
Button:
size_hint_y: .1
text: 'more stuff'
Button:
text: 'Change Res'
size_hint_x:.2
size_hint_y: .2
pos_hint: {'x': .75, 'y': .05}
on_release: root.shrink()
""")
class One(Screen):
def enlarge(self):
Window.size = (500, 600)
sm.current = 'two'
class Two(Screen):
def shrink(self):
Window.size = (300, 400)
sm.current = 'one'
sm = ScreenManager()
sm.add_widget(One(name='one'))
sm.add_widget(Two(name='two'))
class SampleApp(App):
def build(self):
Window.size = (300, 400)
Window.left = 750
Window.top = 40
return sm
if __name__ == '__main__':
SampleApp().run()

How to center buttons in Kivy?

I am new to Python UI programming, and I am trying out Kivy. I want to center some buttons on my screen, but the buttons do not move from the bottom right of the window. I was wondering if anyone could point out what I am doing wrong or am missing?
vgx.kv:
#:kivy 1.9.1
<VgxMainScreen>:
BoxLayout:
size_hint: 0.2,0.2
size: 200, 100
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
<VgxUI>:
AnchorLayout:
anchor_x: 'center'
VgxMainScreen:
size_hint: 0.2,0.2
<VgxApp>:
FloatLayout:
VgxUI:
center: 0.5, 0.5
VgxApp.py:
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
kivy.require('1.9.1')
class VgxMainScreen(Widget):
pass
class VgxUI(Widget):
pass
class VgxApp(App):
def build(self):
return VgxUI()
if __name__ == '__main__':
VgxApp().run()
Edit: Best way to debug what's going on with you widgets is to change their background colors :).
canvas.before:
Color:
rgba: X, X, X, 1
Rectangle:
pos: self.pos
size: self.size
The problem was that your BoxLayout wasn't positioned relatively to its parent even though it was inside it.
What I did here is that I positioned the BoxLayout to its parent's size and position using:
size: self.parent.size
pos: self.parent.pos
I also moved the size property from VgxMainScreen to VgxUI because I assume this is more common use-case (you can have multiple VgxMainScreens each with different size). Full code with background colors:
#:kivy 1.9.1
<VgxMainScreen>:
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1 # red
Rectangle:
pos: self.pos
size: self.size
BoxLayout:
canvas.before:
Color:
rgba: 0, 0, 1, 1 # blue
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size # important!
pos: self.parent.pos # important!
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
<VgxUI>:
AnchorLayout:
canvas.before:
Color:
rgba: 1, 1, 1, 1 # white
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size
anchor_x: 'center'
anchor_y: 'center'
VgxMainScreen:
size: 200, 100
<VgxApp>:
FloatLayout:
VgxUI:
center: 0.5, 0.5
There's yet another solution to this.
You can use RelativeLayout and all widgets inside it will position relatively to this layout.
<VgxMainScreen>:
size_hint: None, None
RelativeLayout:
pos: self.parent.pos
size: self.parent.size
BoxLayout:
orientation: 'vertical'
Button:
text: 'Game Select'
Button:
text: 'Options'
Button:
text: 'Controllers'
I've personally scratched my head many times when using Kivy and wondered why my widgets aren't positioned as I think they should :).
VgxMainScreen is a Widget, so it doesn't apply any positioning or sizing to its children (only Layouts do that), so the BoxLayout has the default position of (0, 0) and size of (100, 100).
Make VgxMainScreen inherit from something else, like a FloatLayout.

Categories

Resources