Kivy: How to check the location of a drag behavior item - python

Not much code yet, but I am trying to make the buttons stay if they are on the right side, but if they are on the left, to disappear. I am not sure if this is possible, but if you are able to help, I would love it!main.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.button import Button
Builder.load_file('main.kv')
class DragButton(DragBehavior, Button):
pass
class MainScreen(Widget):
pass
class WorkingApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
WorkingApp().run()
​
main.kv
<DragButton>
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000
drag_distance: 0
text: 'Hi'
size_hint: (1, .5)
<MainScreen>
BoxLayout:
size: root.width, root.height
orientation: 'horizontal'
BoxLayout:
orientation: 'vertical'
Label:
text: 'Widgets'
size_hint: (1, .25)
DragButton:
DragButton:
DragButton:
Splitter:
sizable_from: 'left'
Label:
text: 'Check If they are here'

One way to do this is to bind a method to the center_x property of the DragButtons. That method can then check the position of the DragButton, and remove it, if it is to the right of the Splitter:
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.uix.behaviors import DragBehavior
from kivy.uix.button import Button
Builder.load_file('main.kv')
class DragButton(DragBehavior, Button):
pass
class MainScreen(Widget):
splitter = ObjectProperty(None)
boxlayout = ObjectProperty(None)
def dragged(self, dragButton):
if dragButton.center_x > self.splitter.x:
self.boxlayout.remove_widget(dragButton)
class WorkingApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
WorkingApp().run()
with some small modifications to the 'kv':
<DragButton>
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000
drag_distance: 0
text: 'Hi'
size_hint: (1, .5)
<MainScreen>
splitter: split
boxlayout: box
BoxLayout:
size: root.width, root.height
orientation: 'horizontal'
BoxLayout:
id: box
orientation: 'vertical'
Label:
text: 'Widgets'
size_hint: (1, .25)
DragButton:
on_center_x: root.dragged(self)
DragButton:
on_center_x: root.dragged(self)
DragButton:
on_center_x: root.dragged(self)
Splitter:
id: split
sizable_from: 'left'
Label:
text: 'Check If they are here'
Key modifications are the addition of ids in the kv for the Splitter and the BoxLayout and methods bound to the center_x property of each DragButton. In the python, ObjectProperties for those new ids and the dragged() method are added to the MainScreen class. The dragged() method just checks if the center of the DragButton is roght of the Splitter and removes that DragButton if it is.

Related

How to get input box content in kivy application

I'm writing a kivy app that lets the user enter and store content, but I've searched the web and can't find a solution
This is the python program
from kivy.app import App
from kivy.graphics import Line, Color
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.config import Config
Config.set('graphics', 'resizable', False)
from kivy.core.window import Window
Window.size = (450, 800)
class DrawCanvasWidget(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
class PaintApp(App):
def build(self):
self.draw_canvas_widget = DrawCanvasWidget()
return self.draw_canvas_widget # 返回root控件
if __name__ == "__main__":
PaintApp().run()
This is the kv program
<DrawCanvasWidget>
orientation: "vertical"
canvas.before:
Color:
rgba: [1,1,1,1]
Rectangle:
pos: self.pos
size: self.size
TextInput:
multiline:False
font_size:20
pos:75,450
size:300,40
allow_copy:False
cursor_color:[0,1,0,1]
Button:
text:'ok'
bold:10
size_hint:None,None
size:100,50
pos:180,380
I want to let the user save the content of the input box after pressing the button
This is the basic idea. Create a function in the draw canvas widget and pass the text of the text input by using the id tag.
<DrawCanvasWidget>
orientation: "vertical"
canvas.before:
Color:
rgba: [1,1,1,1]
Rectangle:
pos: self.pos
size: self.size
TextInput:
id: text_input
multiline:False
font_size:20
pos:75,450
size:300,40
allow_copy:False
cursor_color:[0,1,0,1]
Button:
text:'ok'
bold:10
size_hint:None,None
size:100,50
pos:180,380
on_release: root.submit(text_input.text)
class DrawCanvasWidget(Widget):
def __init__(self,**kwargs):
super().__init__(**kwargs)
def submit(self, text_input):
print(text_input)
# Should print whatever is in the textinput to the terminal
# Here you can save it to your database or do anything with the input.

Kivy Inherit ScreenManager from FloatLayout

I am having trouble understanding how to inherit the functionality of one class into another with Kivy. I understand the error message ('LoadDialog_Directory' object has no attribute 'manager'), but I'm just not grasping exactly how to fix it. I believe I need to do something with the function below, but ultimately, I do not know.
def __init__(self, **kwargs):
super().__init__(**kwargs)
The goal of this script is to be able to select a specific driver as the path for the filechooser. I choose this method vs. others because most were using Kivy 1.11.1, this version has a lot of deprecated functions that do not work with 2.0.
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
def dir_driver(self):
x = self.manager.get_screen("first")
return str(x.ids.drive_dir.text)
class FirstWindow(Screen):
def get_drives(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.dir_driver()
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
text: "Root Drive"
halign: 'center'
option_cls: "MyOption"
values: root.get_drives()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Side Note: The reason for the extra blank TextInput is because the Spinner will not function (show drivers) if it is taking too much of the App.
After a few hours of trail and error I finally got it to work, but I have no idea why it works. Here is what changed:
New variable in class LoadDialog_Directory(FloatLayout) - input_pth
Called input_pth into my content variable within class FirstWindow(Screen) function show_load_directory(self)
Set my filechooser.path to root.input_pth in the main.kv file.
I do not understand how the input_pth variable within content is able to reference the class LoadDialog_Directory(FloatLayout) without having to pass something like:
def __init__(self, input_pth, **kwargs):
super(LoadDialog_Directory, self).__init__()
self.input_pth = input_pth
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
input_pth = StringProperty()
load = ObjectProperty()
cancel = ObjectProperty()
class FirstWindow(Screen):
def drive_list(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup, input_pth=self.ids.drive_dir.text)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.input_pth
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(filechooser.path, filechooser.selection)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
halign: 'center'
option_cls: "MyOption"
values: root.drive_list()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:

How to keep the same label text between two screens kivy

I'm new to kivy and python so my code isn't perfect.
I'm trying to make a program with 2 screens, a first screen where there is a label with a text that is not defined and that can change and a second screen that keeps the same text as the first screen.
I've been searching for a week and I tried to make a global variable that I edit and that becomes the text of the second label but it doesn't work.
I also tried with String. property () or object. property () but I didn't get any more results and I didn't really understand how to use it.
Any help would be welcome <3
(sorry for my english)
Here is a part of my code that I have simplified as much as possible:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
text: ""
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid2 : ratio
FloatLayout:
Label:
id:ratio
text: str(root.texte2())
pos: 0,90
font_size:30
color:1,0,0,1
""")
u=""
class MenuScreen(Screen):
def texte(self):
global u
u= self.label_wid.text = 'exemple'
pass
class SettingsScreen(Screen):
def texte2(self):
self.label_wid2.text=u
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
Quizz().run()
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string("""
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
text: ""
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid : ratio
FloatLayout:
Label:
id:ratio
text: root.texte2()
pos: 0,90
font_size:30
color:1,0,0,1
""")
u=""
class MenuScreen(Screen):
def texte(self):
global u
u= self.label_wid.text = 'exemple'
pass
class SettingsScreen(Screen):
def texte2(self, text):
u
pass
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
return sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
Quizz().run() ```
Using a StringProperty in the ScreenManager and another in SettingsScreen provide a source of text for both Labels in the different Screens. And changing the StringProperty can change both Labels:
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import StringProperty
from kivy.core.window import Window
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MenuScreen(Screen):
pass
class SettingsScreen(Screen):
# this is the text to be used when the first Label text is not '1'
theOtherText = StringProperty('Not One')
class MyScreenManager(ScreenManager):
# This is the text that both Labels may display
theText = StringProperty('0')
kv = """
MyScreenManager:
MenuScreen:
name: 'menu'
SettingsScreen:
name: 'settings'
<MenuScreen>:
label_wid : ratio
FloatLayout:
Button:
text: "options"
pos: 270, 240
size_hint: .30, .10
background_color: 0,1,0,0.75
on_press: root.manager.current = 'settings'
Label:
id:ratio
# Use theText Property from MyScreenManager
text: root.manager.theText
pos: 0,90
font_size:30
color:1,0,0,1
<SettingsScreen>:
label_wid : ratio
FloatLayout:
Label:
id:ratio
# Use theText Property from MyScreenManager
text: '1' if root.manager.theText == '1' else root.theOtherText
pos: 0,90
font_size:30
color:1,0,0,1
"""
class Quizz(App):
def build(self):
self.title = 'Quizz'
Window.clearcolor = (0, 1, 1, 0.25)
Clock.schedule_once(self.change_text, 4)
return Builder.load_string(kv)
def change_text(self, dt):
print('changing text')
self.root.theText = '1'
if __name__ == '__main__':
Quizz().run()
Building the MyScreenManager in the kv eliminates the need to check if the manager attribute of the Screens is set. That simplifies the logic in the kv. Then the line in kv:
text: '1' if root.manager.theText == '1' else root.theOtherText
sets the text of the second Label to 1 if the first Label text is 1, otherwise it is set to the value of theOtherText in the SettingsScreen.
I have added a Clock.schedule_once() just for testing.

How to ensure proper functionality when adding widgets to Kivy

I am attempting to create a Kivy/Python application which gets the user to push a button on one screen, and in turn adds several widgets to the next screen. However, when I run the script, the button appears and can be clicked on the first page, and the widgets are visible on the second page, but they can not be interacted with.
Here is an example of my code, any help is appreciated:
py:
import kivy
kivy.require('1.11.1')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.properties import ListProperty, StringProperty, ObjectProperty
from kivy.core.window import Window
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.button import Button
from kivy.uix.spinner import Spinner
sm = None
class RcGUITestApp(App):
def build(self):
global sm
Window.clearcolor = (0, 0, 0.35, 1)
sm = ScreenManager()
sm.add_widget(NewJobPage(name="new_job_page"))
sm.add_widget(IPMConfirmPage(name = "ipm_confirm_page"))
return sm
class NewJobPage(Screen):
def retrieve(self):
global rc
global sm
sm.get_screen("ipm_confirm_page").displayIPM()
class IPMConfirmPage(Screen):
grid = ObjectProperty(None)
def displayIPM(self):
for ipm in range(50):
self.grid.add_widget(Label(text="A"))
self.grid.add_widget(Spinner(text="B"))
self.grid.add_widget(Spinner(text="C"))
def main():
rc_gui = RcGUITestApp()
rc_gui.run()
if __name__ == "__main__":
main()
kv:
<NewJobPage>:
BoxLayout:
orientation: "horizontal"
size_hint: (1, .35)
Button:
text: "RETRIEVE"
pos_hint: {"center_x": .5, "center_y": .5}
size_hint: (.33, .33)
color: (1,1,1,1)
on_press:
root.retrieve()
root.manager.transition.direction = "left"
root.manager.transition.duration = .8
root.manager.current = "ipm_confirm_page"
<IPMConfirmPage>:
grid: Grid
GridLayout:
cols: 1
size_hint_x: 1
padding: 10
spacing: 10
BoxLayout:
size_hint: (1, 0.1)
Label:
text: "IPM"
Label:
text: "CD#"
Label:
text: "CONDUCTOR"
ScrollView:
GridLayout:
id: Grid
cols: 3
height: self.minimum_height
size_hint: 1, None
spacing: 50
padding: 30
First Page output
Second Page output
You need to provide the height of each widget added to grid. There are two solutions to the problem.
Method 1 - kv file
This method involves changing only the kv file by adding row_force_default: True and row_default_height: 40.
Snippets - kv file
ScrollView:
GridLayout:
row_force_default: True
row_default_height: 40
...
Method 2 - py file
This method involves changing only the py file by adding size_hint_y=None and height=40.
Snippets
def displayIPM(self):
for ipm in range(50):
self.grid.add_widget(Label(text="A", size_hint_y=None, height=40))
self.grid.add_widget(Spinner(text="B", size_hint_y=None, height=40))
self.grid.add_widget(Spinner(text="C", size_hint_y=None, height=40))

Read the text from Textinput dynamically when text changed in text box?

I'm new to kivy , I'm trying to write an application using kivy in python, I got struck at one point where i have to read text from textinput whenever it is changed and based on that i want to implement my button's functionality - I have gone through all the documentation but i couldn't figure out how to do it - Can any body tell me how can I resolve this or am I missing something?
from __future__ import print_function
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.core.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import *
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
import walascan
from kivy.clock import Clock
import os
kv = """
<KartScan>:
IntroScreen:
<IntroScreen#Screen>:
orientation: 'horizontal'
name: 'introscreen'
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'index.png'
AnchorLayout:
anchor_x: 'center'
anchor_y: 'center'
BoxLayout:
orientation:'horizontal'
size_hint: .5, .1
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
spacing: 20
pos_hint: {'center_x':.8, 'center_y': .8}
AnchorLayout:
anchor_x: 'left'
size_hint_x: .5
TextInput:
id: waybill
width: 20
text: "Enter Waybill No."
multiline: False
height: self.minimum_height
size_hint_y: None
font_size: 30
focus: True
on_text_validate: app.on_waybill()
AnchorLayout:
anchor_x: 'right'
size_hint_x: None
Button:
size_hint: None, None
height: 50
width: self.texture_size[0]
padding: 10, 10
text: "Add"
on_press:app.buttonClicked()
on_release: root.current = 'mainpage'
AnchorLayout:
anchor_x: 'right'
size_hint_x: None
Button:
size_hint: None, None
height: 50
width: self.texture_size[0]
padding: 10, 10
text: "Compare"
on_press:app.buttonClicked()
on_release: root.current = 'mainpage'
"""
Builder.load_string(kv)
waybill = TextInput(text="Enter Waybill No.", multiline=False)
class KartScan(FloatLayout):
def __init__(self, **kwargs):
super(KartScan, self).__init__(**kwargs)
self.register_event_type('on_text_validate')
def on_text(self, *args):
print('new value is ', waybill.text)
def on_text_validate(self):
pass
def on_focus(self, obj, focused):
if not focused:
self.dispatch('on_text_validate')
class KartScanApp(App):
def build(self):
return KartScan()
def buttonClicked(self):
popup = Popup(title='Result',
content=Label(text=self.on_waybill()),
size_hint=(None, None), size=(100, 100))
popup.bind()
popup.open()
def getwlbtstate(self):
return walascan.mainAntennas()
def on_waybill(self):
waybill.bind(text=KartScan.on_text_validate)
# popup = Popup(title='Result',
# content=Label(text=waybill.text),
# size_hint=(None, None), size=(100, 100))
# popup.bind()
# popup.open()
return waybill.text
if __name__ == '__main__':
KartScanApp().run()
kv file
TextInput:
on_text: print(self.text)
You could use on_text to call a function when the text changes. I think it sometimes even triggers when no change has occured.
TextInput has a text property, and in kivy, all properties have associated on_xxx events, that you can bind to to react to them, so when reading the doc, even if it's not explicitly mentionned for them, everytime you see something is defined as a Property (or any subclass of it, like StringPropertyNumericProperty,ObjectProperty,AliasProperty`).
TextInput:
on_text: do_something(self.text)
or in python
t = TextInput(text='', on_text=do_something)
or
t = TextInput(text='')
t.bind(on_text=do_something)

Categories

Resources