when a user click on a widget on my application, the keyboard that appears can hide it if the widget is too low.
I tried ScrollView.scroll_to() to ask for a focus on the widget, it works but it doesn't take the keyboard in the equation.
Before I click on "Raison Sociale".
After I click on "Raison Sociale
So the widget is hidded by the keyboard.
I don't know if a function like this already exist.
If widget.hiddedByKeyboard():
widget.show_taking_consideration_of_the_keyboard()
There is also the problem of the widgets at the end of the Screen, if we try to show them by scrolling down, the ScrollView will try to go up again even if that will hide the widget.
Ahh the classic, the keyboard covers the widget problem. I encountered this and here's what I did to solve it:
In my KV file I had something like this:
<MainWindow>:
... [A BUNCH OF WIDGETS] ...
ScrollView:
BoxLayout:
orientation: 'vertical'
size_hint: 1, None
height: self.minimum_height
... [A BUNCH OF WIDGETS] ...
Widget: # The important widget
id: buffer
size_hint: (1, None)
height: root.buffer
I added at the bottom of the ScrollView a blank Widget whose height was defined by a NumericProperty in the top level widget (Window - a BoxLayout).
In the Python file I had:
from kivy.core.window import Window # Need to import this
class MainWindow(BoxLayout):
buffer = NumericProperty()
Then when I requested the keyboard, I made sure to set buffer = Window.keyboard_height. This adjusted the height of blank Widget to the height of the keyboard, essentially creating extra room for the keyboard. This gives the user extra scrolling room to position the keyboard.
You can combine this implementation with the ScrollView.scroll_to() method to create a really nice effect. Essentially pass ScrollView.scroll_to(text_input_widget) when the user clicks on the TextInput. This will automatically scroll the ScrollView down so that the TextInput is at the top and the keyboard is below it.
This probably isn't the cleanest implementation (I could probably do it better now), but the concept of creating a blank Widget whose size is the height of the keyboard would be the same.
I found a better way, this attribute will do all the job.
from kivy.core.window import Window
Window.softinput_mode = "below_target"
Related
I'm trying to get the size of the rectangle from the canvas called Canvas_Widget class, but no matter what I do, it keeps on giving an error
the code that I wrote and the description of the error are given in the code as a comment.
source file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.core.window import Window
from kivy.metrics import dp
Window.size= dp(500),dp(500)
class Canvas_Widget(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
class MainWidget(BoxLayout):
def __init__(self,**kwargs):
super(MainWidget, self).__init__(**kwargs)
def minimizer(self,button):
button.background_color=(1,0,0,1)
#here i want to try and print the size of that rect id that i created
# i tried "print(self.ids.Canvas.ids.rect.size)"
"""the error says: (((File "f:\canvas\1\main.py", line 16, in minimizer
print(canvas.ids.rect.size)
File "kivy\properties.pyx", line 964, in kivy.properties.ObservableDict.__getattr__
AttributeError: 'super' object has no attribute '__getattr__')))"""
def minimizer_release(self,button):
button.background_color=(.3,.3,1,1)
class mynewApp(App):
pass
mynewApp().run()
here is the kivy file:
MainWidget:
<Canvas_Widget>:
canvas:
Color:
rgb:.4,.4,.4,1
Rectangle:
id:rect #this is the id that Im trying to share with the MainWidget
pos:dp(100),dp(100)
size:self.width - dp(200),self.height - dp(200)
Color:
rgb:0,1,0,1
Line:
points:(dp(100),self.height - dp(100),self.width - dp(100),dp(100))
Line:
points:(dp(100),dp(100),self.width - dp(100),self.height - dp(100))
<MainWidget>:
Button:
text:'press'
on_press:root.minimizer(self)
background_color:(.3,.3,1,1)
on_release: root.minimizer_release(self)
Canvas_Widget:
id:Canvas
here is also the problem with the placement of the canvas on the screen. its suppose to be on the right side, but it's on the left side, on the button:
image 1
image 2
by the way, I'm sorry to ask for too much. it's just that I really want to know how to fix this problem.
thanks
First thing to notice that in kvlang binding happens by Builder automatically (at least from developer's side). That's why it's convenient to use this language for design purpose. From kivy doc.,
As your application grows more complex, it’s common that the construction of widget trees and explicit declaration of bindings becomes verbose and hard to maintain. The KV Language is an attempt to overcome these shortcomings. The KV language, sometimes called kvlang or the kivy language, allows you to create your widget tree in a declarative way and to bind widget properties to each other or to callbacks in a natural manner.
Now in kvlang when you do something like this :
Rectangle:
pos:dp(100),dp(100)
in the canvas instructions, you are basically instructing to draw a rectangle at (100, 100) in the window. But what you want (as it seems) is to draw that rectangle where the widget is and shifted by (100, 100) from its position.
Now as you used BoxLayout as container, each of its child's position, size are managed by itself (if not provided explicitly). So to reflect that changes you need to change the above lines of code by the following,
Rectangle:
pos: self.x+dp(100), self.y+dp(100)
This will reposition the rectangle (automatically) whenever the position of widget (x, y) changes.
Thus the changes you need in your kvlang are,
<Canvas_Widget>:
canvas:
Color:
rgb:.4,.4,.4,1
Rectangle:
# id:rect # Will not work here.
# pos:dp(100),dp(100)
pos: self.x+dp(100), self.y+dp(100)
size: self.width - dp(200), self.height - dp(200)
Color:
rgb:0,1,0,1
Line:
# points:(dp(100),self.height - dp(100),self.width - dp(100),dp(100))
points: (self.x+dp(100), self.y+self.height-dp(100), self.x+self.width-dp(100),self.y+dp(100))
Line:
# points:(dp(100),dp(100),self.width - dp(100),self.height - dp(100))
points: (self.x+dp(100), self.y+dp(100),self.x+self.width-dp(100), self.y+self.height-dp(100
I'm working on integrating some robots into my work's warehouse. I've created a Kivy GUI to operate these robots. Normally when you press a button in Kivy, the button shape shifts from rectangular to a rounded rectangle and the button color changes. It maintains this response if I add in simple functionality, like it printing words when pressed. However, when I add in code to control the robots, it loses this visual indication. The button works - when pressed it executes the proper function, but there is no change in the button's appearance to indicate it has been pressed.
I don't know how much code I can provide since it is proprietary. I'm using the robot company's API and SDK to create this GUI.
I've tried making buttons both using a .kv file and without. I've tried manually adding in color changes when the button is pressed - no luck. I've also tried making transparency changes and text changes. Basically I can't get any visual change to happen to the button when it is pressed while it calls this function.
from my .kv file (sorry the formatting came out weird):
< Button >:
font_size: 30
color: 1,1,1,1
size_hint: 0.15,0.1
< FloatLayout >:
Button:
text: "Clear Error"
font_size: 20
pos_hint: {"x":0.85, "top":0.1}
background_normal: ''
background_color: .2,.8,.2,1
on_press: app.ClearError()
from my .py file:
def ClearError(self):
robot_name1 = "robot name"
print("CLEAR ERROR")
robot = Robot.load(robot_name1, client=client)
if robot.error_status is not None:
print("Error status!")
robot.error_status = None
robot.save(client=client)
I have a textinput widget that looks like this:
<ReaderWidget>:
Label:
text: 'Please scan EBT card'
font_size: root.height/8
size: self.texture_size
bold: True
color: 0, 0.70, 0.93, 1
TextInput:
focus: True
password: True
multiline: False
cursor: 0, 0
The widget is dynamically added to the layout based on the user pressing a button in another widget. Currently the user has to point the mouse/finger into the text box before entering text, and I want the cursor to be in the text box ready to receive text without the user having to indicate by mouse press. Is there a way to do this?
It seems like focus : True should do it. But it doesn't seem to.
I know this is old but I found this question when I was trying to do something very similar. My code for adding the TextInput (and setting it's focus) was in the on_press handler for a button. A press would cause the TextInput to be added to the layout and it's focus set, but then it would lose focus when the button was released. Moving my code to on_release fixed the problem.
This worked for me in kivy 1.9.0:
def show_keyboard(event):
text_input.focus = True
Clock.schedule_once(show_keyboard)
If text_input.focus is set directly, it doesn't seem to work. Perhaps this is a bug in kivy?
I had the same issue, but not for a button, so the on_release was not an option.
If you want to do it with the on_touch_down method, you can focus the widget and add the current touch to be ignored for focusing:
def on_touch_down(self, touch):
self.focus = True
FocusBehavior.ignored_touch.append(touch)
Of course you also need to import FocusBehavior:
from kivy.uix.behaviors.focus import FocusBehavior
You can read more here: https://kivy.org/doc/stable/api-kivy.uix.behaviors.focus.html
I would like to create an app in kivy with widgets in a grid and I want to be able to set some widgets to be larger - to occupy more than one cell in a grid.
GridLayout seems the most appropriate, but it seems to not support placing widgets in more than one cell.
To be more specific I want behavour similar to the grid geometry manager from Tkinter, when setting columnspan or rowspan to more than 1.
Like this:
(widget)(widget)(widget)
( bigger widget )(widget)
...
I would prefer to be able to do this using existing kivy layouts instead of having to write my own layout class to handle this, but if no other option is possible, this is also ok.
Another option here is to have a GridLayout with 1 column and populate each row with a BoxLayout with orientation="horizontal". You can then format each BoxLayout (row) as you want.
For more info on the BoxLayout: http://kivy.org/docs/api-kivy.uix.boxlayout.html
I don't think a GridLayout is really suitable, it just isn't designed for quite that usage.
If I personally had to do this, I'd probably make my own Layout class, it wouldn't need a very complicated do_layout method.
One possible start point would be the SparseGridLayout I made a while ago. You could very easily add column and row span properties to it...actually, I'll probably add them myself now that you've given me the idea!
That might not be ideal if you have a big grid full of widgets, in which case something similar to a gridlayout might be better, or possibly a combination of multiple layouts if the spanning widgets are in a particular pattern.
Lets assume you have a GridLayout with two columns and you want to span the first row.You can add Two FloatLayout whereby the first FloatLayout will contain the widget you would like to span while the second Layout will have row and height values to zero.This would archive a span effect
here is an example of .kv span effect
GridLayout:
cols:2
FloatLayout: # The first FloatLayout in the first column in the gridLayout
size_hint:None,None
size: 0,50
BoxLayout:
size_hint: None,None
size: root.width-40,50
pos_hint: {'x':.5,'center_y':.5}
BoxLayout:
padding:0,0,5,0
Label:
id:lbl_unknown
text:'Accession number :'
TextInput:
text:''
FloatLayout: # The second FloatLayout in the second column of the gridLayout
size_hint:None,None
size:0,0
Label:
text:'Label 1:'
TextInput:
Label:
text:'Label 2:'
TextInput:
An easy work around is to pack BoxLayout objects into any type of parent layout you want using your better judgement to decide the orientation of each subsequent BoxLayout
(decided to use box layouts for almost all of my own project over any gridlayout)
This is my class:
class AuthPage(BoxLayout):
def __init__(self, **kwargs):
super(AuthPage, self).__init__(**kwargs)
self.orientation = 'vertical'
self.add_widget(Label(text='Authenticate'))
unameRow = BoxLayout(orientation='horizontal')
unameRow.add_widget(Label(text='User Name'))
unameRow.username = TextInput(multiline=False)
unameRow.add_widget(unameRow.username)
self.add_widget(unameRow)
pwordRow = BoxLayout(orientation='horizontal')
pwordRow.add_widget(Label(text='password'))
pwordRow.password = TextInput(password=True, multiline=False)
pwordRow.add_widget(pwordRow.password)
self.add_widget(pwordRow)
self.add_widget(Button(text='authenticate'))
I've created a ScreenManager, and created several Screen instances for that ScreenManager.
I'd like each Screen to display a GridLayout class. For example, let's say you have:
class MainScreen(Screen):
...
class MainLayout(GridLayout):
...
When MainScreen is the active screen, I'd like MainLayout to be shown.
Is there a way to do this purely in python (i.e. without markup)? Thank you.
Is there a way to do this purely in python (i.e. without markup)? Thank you.
You never need to use kivy language (I assume that's what you mean by markup), though it's highly recommended where possible because it makes lots of stuff easier.
Nonetheless, to actually answer your question, all you have to do is add your gridlayout widget to your screen widget, something like
mainscreen = MainScreen()
mainlayout = MainLayout()
mainscreen.add_widget(mainlayout)
Then when you set the current screen in your screenmanager to be mainscreen, you should see the GridLayout.
Edit: In case it's unclear, this is in general the way you add widgets to other widgets. When you see an example in kv language like
<MyScreen>:
GridLayout:
...
...ultimately that gets translated to something much like the above example code - an instance of MyScreen is created and a GridLayout is added to it with add_widget.