Unbinding Window.grab_mouse - python

There is an example of how to bind and unbind files drag&drop from system into Kivy app.
(Perhaps this can be done better, but now it is important that it works)
There are 2 buttons, the left one (roughly speaking) enables files dropping, and the right one - turns this function off.
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
BoxLayout:
Button:
text: 'bind dropfile'
on_press: app.bind_dropfile()
Button:
text: 'unbind dropfile'
on_press: app.unbind_dropfile()
'''
class DropFileApp(App):
def build(self):
self.root = Builder.load_string(KV)
def manage_dropfile(self, window, fn):
print('do something')
def bind_dropfile(self):
Window.bind(on_dropfile=self.manage_dropfile)
print ('bound!')
def unbind_dropfile(self):
Window.unbind(on_dropfile=self.manage_dropfile)
print ('unbound!')
if __name__ == '__main__':
DropFileApp().run()
There is another similar example.
The app also has two buttons.
I expect that when clicking on the left button, the mouse will become limited to the app window area (will not be able to exit it), and when I click on the right button, this function will be disabled.
But only the left button works.
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
BoxLayout:
Button:
text: 'bind grab_mouse'
on_press: app.bind_grab_mouse()
Button:
text: 'unbind grab_mouse'
on_press: app.unbind_grab_mouse()
'''
class DropFileApp(App):
def build(self):
self.root = Builder.load_string(KV)
def manage_grab_mouse(self, instance):
print('do something')
Window.grab_mouse()
def bind_grab_mouse(self):
Window.bind(on_cursor_leave=self.manage_grab_mouse)
print ('bound!')
def unbind_grab_mouse(self):
Window.unbind(on_cursor_leave=self.manage_grab_mouse)
print ('unbound!')
if __name__ == '__main__':
DropFileApp().run()
What could be the problem?

You are deactivating the connection between the on_cursor_leave event and the manage_grab_mouse function, but you are not disabling the grab_mouse functionality. What you have to do is call grab_mouse() in bind_grab_mouse() function and ungrab_mouse() in ungrab_mouse() function, it is not necessary to create the function manage_grab_mouse.
from kivy.app import App
from kivy.core.window import Window
from kivy.lang import Builder
KV = '''
BoxLayout:
Button:
text: 'bind grab_mouse'
on_press: app.bind_grab_mouse()
Button:
text: 'unbind grab_mouse'
on_press: app.unbind_grab_mouse()
'''
class DropFileApp(App):
def build(self):
self.root = Builder.load_string(KV)
def bind_grab_mouse(self):
Window.grab_mouse()
def unbind_grab_mouse(self):
Window.ungrab_mouse()
if __name__ == '__main__':
DropFileApp().run()

Related

How can I display the new value made by input for the next screen in Kivy

I have been trying to make this code work. Im using ScreenManager to manage my screen.
I want the Input I entered on the first screen to be displayed the next screen. But instead, it just shows the initial value, and it doesn't change to the Inputted value.
Here is the Code i have done
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.clock import Clock
Builder.load_string("""
<MenuScreen>:
promptObject: prompts
BoxLayout:
orientation: 'horizontal'
TextInput:
id: prompts
pos: 20,20
Button:
text: "Enter Prompt"
pos: 30,30
size: 100, 30
on_press: root.submit()
<Newscreen>
BoxLayout:
orientation: 'vertical'
TextInput:
id: display_output
text: root.output
readonly: True
""")
class MenuScreen(Screen):
promptObject = ObjectProperty()
prompt = ''
def submit(self):
prompt = self.promptObject.text
global result
result = prompt
sm.add_widget(NewScreen(name="Settings"))
sm.switch_to(sm.get_screen("Settings"))
NewScreen.display(self)
class NewScreen(Screen):
output = "testing testing"
def display(self):
self.output = result
print(result) #To test if it works
class TestApp(App):
def build(self):
global sm
sm = ScreenManager()
sm.add_widget(MenuScreen(name='menu'))
return sm
if __name__ == '__main__':
TestApp().run()
I'm also thinking if i can instead Declare the Layout for the second screen later right before i call for the next Screen. Maybe that could work but if you have other method, it would be nice to see it.
thank you for the concise single-file example. this is a very helpful way to submit a kivy question. I have modified and tested the below app with various changes.
I changed the root.submit to app.submit. This is not strictly required, it is just a choice in this example to put the logic in the main app. it is also possible to use root.submit and put the logic in the widget but one would have to pass a reference to the screen manager into that widget in that case.
imported TextInput object instead of using ObjectProperty. when using an IDE it is helpful to declare objects with the specific type because it enables auto-complete
assigned the ScreenManager to self.sm so this object is available throughout the app.
finally, got rid of any reference to global. I think it is better to avoid use of this keyword and explicitly create the variable at the highest level where you need it and pass the value into the objects requiring it.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
# from kivy.properties import ObjectProperty
from kivy.uix.textinput import TextInput
from kivy.properties import StringProperty
from kivy.clock import Clock
Builder.load_string("""
<MenuScreen>:
promptObject: prompts
BoxLayout:
orientation: 'horizontal'
TextInput:
id: prompts
pos: 20,20
Button:
text: "Enter Prompt"
pos: 30,30
size: 100, 30
on_press: app.submit()
<Newscreen>
BoxLayout:
orientation: 'vertical'
TextInput:
id: display_output
text: root.output
readonly: True
""")
class MenuScreen(Screen):
promptObject = TextInput()
class NewScreen(Screen):
output = StringProperty()
def __init__(self, **kw):
super().__init__(**kw)
def display(self, result):
# set the string property equal to the value you sent
self.output = result
print(result) # To test if it works
class TestApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# create screen manager with self so that you have access
# anywhere inside the App
self.sm = ScreenManager()
# create the main screen
self.menu_screen = MenuScreen(name='menu')
# this could be deferred, or created at initialization
self.settings_screen = NewScreen(name='Settings')
#
def submit(self):
prompt = self.menu_screen.promptObject.text
result = prompt
# optional, deferred creation
# self.settings_screen = NewScreen(name='Settings')
# add to the screen manager
self.sm.add_widget(self.settings_screen)
# enter the value into your other screen
self.settings_screen.display(result)
# switch to this screen
self.sm.current="Settings"
def build(self) -> ScreenManager:
# could create this screen right away, depending...
# self.sm.add_widget(self.settings_screen)
# of course you need the main screen
self.sm.add_widget(self.menu_screen)
# redundant, unless you create all screens at the beginning
self.sm.current = 'menu'
return self.sm
if __name__ == '__main__':
TestApp().run()

Popup - code conversion to KV and PY files

I would like to have code in KV and PY files that will display a YES/NO popup that I can instantiate anywhere in my app to pop the question.
Will a good kindly person help me to convert code I found on internet to KV file and PY file content.
I would like to be able to create an instance of this popup at any point in my app for display.
This is the sample I am working from:
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty()
def __init__(self,**kwargs):
self.register_event_type('on_answer')
super(ConfirmPopup,self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupTest(App):
def build(self):
content = ConfirmPopup(text='Do You Love Kivy?')
content.bind(on_answer=self._on_answer)
self.popup = Popup(title="Answer Question",
content=content,
size_hint=(None, None),
size=(480,400),
auto_dismiss= False)
self.popup.open()
def _on_answer(self, instance, answer):
print "USER ANSWER: " , repr(answer)
self.popup.dismiss()
if __name__ == '__main__':
PopupTest().run()
I have tried the following:
KV file:
<YesNoPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: root.dispatch('on_answer','yes')
Button:
text: 'No'
on_release: root.dispatch('on_answer', 'no')
and PY file:
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
class YesNoPopup(GridLayout):
text = StringProperty()
def __init__(self, **kwargs):
self.register_event_type('on_answer')
super(YesNoPopup, self).__init__(**kwargs)
def on_answer(self, *args):
pass
class PopupTest:
def build(self):
content = YesNoPopup(text='Are you sure?')
content.bind(on_answer=self._on_answer)
self.popup = Popup(title='PIN',
content=content,
size_hint=(0.6, 0.4),
auto_dismiss=False)
self.popup.open()
def _on_answer(self, instance, answer):
print
"USER ANSWER: ", repr(answer)
self.dismiss()
Then in the App
def pin_action(self):
pop = PopupTest()
pop.build()
But this only shows popup with Title and separator bar.
I will appreciate explanations of why you do what you do so that I as an 82 year old can learn and understand since my OO education is 22 years old and my Python Kivy is still in the learning stage.
Thanks a million
You can simplify your code by just calling the _on_answer() method directly from the on_release: of the Button. Here is a modified version of your code that does that:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.properties import StringProperty
Builder.load_string('''
<ConfirmPopup>:
cols:1
Label:
text: root.text
GridLayout:
cols: 2
size_hint_y: None
height: '44sp'
Button:
text: 'Yes'
on_release: app._on_answer(root, 'yes')
Button:
text: 'No'
on_release: app._on_answer(root, 'no')
''')
class ConfirmPopup(GridLayout):
text = StringProperty()
class PopupTest(App):
def build(self):
content = ConfirmPopup(text='Do You Love Kivy?')
self.popup = Popup(title="Answer Question",
content=content,
size_hint=(None, None),
size=(480, 400),
auto_dismiss=False)
self.popup.open()
def _on_answer(self, instance, answer):
print("USER ANSWER: ", repr(answer))
self.popup.dismiss()
if __name__ == '__main__':
PopupTest().run()
Now that I understand your question, the main problem is just that the bind() method creates a WeakMethod for the bound method. In your code:
def pin_action(self):
pop = PopupTest()
pop.build()
An instance of PopupTest is created (which contains the bound _on_answer() method). But after the build() method of PopupTest is executed, the Popup is opened and the pop variable gets garbage collected. So the _on_answer() method is also garbage collected as a result. Since bind() creates a WeakMethod, there is no method left for the dispatched event to trigger.
There are two ways (that I know of) to fix this issue. One is to just modify the pin_action() method to retain a reference to PopTest so that is does not get garbage collected:
def pin_action(self):
self.pop = PopupTest()
self.pop.build()
Another way is to use fbind() instead of bind(), since fbind() keeps a reference to the bound method, so it cannot be garbage collected:
class PopupTest:
def build(self):
content = YesNoPopup(text='Are you sure?')
# content.bind(on_answer=self._on_answer)
content.fbind('on_answer', self._on_answer)

How to simulate user action on kivy widget ? (click on a button by example)

I want to simulate the user action and after looking in the doc, I saw the Clock.schedule_once(my_callback, 1).
.
Question: Is this the only way to do it ? I would have prefered something like my_button.press().
.
I want to press the button and then, it will call my_callback, I don't want to directly call my_callback.
(Because if I do this and my callback evolve, I lost the good reference)
PS: I saw that there was the possibility to use Clock.create_trigger(my_callback) but it didn't seems to be the good solution here.
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.clock import Clock
KV_APP = '''
BoxLayout:
Button:
id: my_button
text: "Click on me"
'''
class TestApp(MDApp):
def build(self):
self.screen = Builder.load_string(KV_APP)
self.screen.ids["my_button"].bind(on_press=self.on_button_click)
event = Clock.schedule_once(self.on_button_click, 1)
return self.screen
def on_button_click(self, instance):
print("hello there !")
if __name__ == '__main__':
TestApp().run()
You can create an Event and dispatch it. Here is an example:
from kivy.app import App
from kivy.clock import Clock
from kivy.input.providers.mouse import MouseMotionEvent
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
class MyBoxLayout(BoxLayout):
def mydispatchAnEvent(self, *args):
# create and dispatch a fake event
touch = MouseMotionEvent(None, 123, (123, 456)) # args are device, id, spos
touch.button = 'left'
touch.pos = (321, 654)
self.dispatch('on_touch_down', touch)
theRoot = Builder.load_string('''
MyBoxLayout:
orientation: 'vertical'
Label:
text: 'Button below should demonstrate receiving touch event'
Button:
text: 'Waiting for event'
on_press: print('touched')
''')
class EventDispatcherPlayApp(App):
def build(self):
Clock.schedule_once(theRoot.mydispatchAnEvent, 2)
return theRoot
EventDispatcherPlayApp().run()

How can I print the position of the pointer every time the mouse is moved over a widget in kivy?

I want to print out the position of the pointer (mouse) everytime it passes over a widget. Actually to be more specific I'd like to print the current position of the mouse everytime it moves. Is that at all possible?
Right now all I've managed to do is:
Print the position of the mouse whenever the mouse is clicked
(in two different ways)
Print the position of the widget when it's clicked
I'm sure it's easier than I think, thanks in advance.
from kivy.core.window import Window
from kivy.uix.widget import Widget
from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.input.motionevent import MotionEvent
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
def on_mouse_pos(self, pos):
if Window.mouse_pos == self.ids.example_button:
print("You've made it")
# With out this if statement it does print the position every time
# That the mouse is moved. But can I print "You've made it" every time
# that the mouse is moved over the position of a widget?
print(pos)
def test_print(self):
print(str(self.ids.example_button.pos))
Window.bind(mouse_pos = on_mouse_pos)
class MyScreenManager(Screen):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
<MainScreen>:
name: 'Example'
Button:
id: example_button
text: 'Print position of widget when mouse touches it'
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .5}
on_release: root.test_print()
''')
class TestApp(App):
def build(self):
self.title = 'Example Application'
return root_widget
if __name__ == '__main__':
TestApp().run()
A possible solution is to use mouse_pos:
from kivy.core.window import Window
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
def on_mouse_pos(self, pos):
print(pos)
Window.bind(mouse_pos = on_mouse_pos)
class MyScreenManager(Screen):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
<MainScreen>:
name: 'Example'
Button:
id: example_button
text: 'I am button'
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .5}
''')
class TestApp(App):
def build(self):
self.title = 'Example Application'
return root_widget
if __name__ == '__main__':
TestApp().run()
Update:
If you want to verify that the mouse point is inside a widget you must use collide_point.
from kivy.core.window import Window
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
Window.bind(mouse_pos = self.on_mouse_pos)
def on_mouse_pos(self, instance, pos):
if self.ids.example_button.collide_point(*pos):
print("You've made it")
class MyScreenManager(Screen):
pass
root_widget = Builder.load_string('''
MyScreenManager:
MainScreen:
<MainScreen>:
name: 'Example'
Button:
id: example_button
text: 'I am button'
size_hint: 1, .05
pos_hint: {'x': 0, 'y': .5}
''')
class TestApp(App):
def build(self):
self.title = 'Example Application'
return root_widget
if __name__ == '__main__':
TestApp().run()

Not displaying Time in label in kivy

I am kind of new to kivy and i am trying to display time in it
Here is the python code snippet(removed the unnecessary screens/parts for arbitrary reasons):
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
import time
from kivy.uix.label import Label
class MyScreen(Screen):
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenManager()
Clock.schedule_interval(x.update, 1)
root.add_widget(password(name='Screen0'))
root.add_widget(Correct(name='Screena'))
root.add_widget(MyScreen(name='Screen1'))
s=[x,root]
for i in s:
return i
if __name__ == '__main__':
MyApp().run()
.kv file(removed the unnecessary screens/parts for arbitrary reasons):
MyScreen:
<MyScreen>: #
timeb:time_box
BoxLayout:
orientation: "horizontal"
pos_hint: {'top':1}
height: "40dp"
size_hint_y: None
Label:
id:time_box
text:root.update()
size_hint_x: 6
font_size:30
font_name:"Roboto-Light.ttf"
As you can see in the code i have added a few screens but the My screen is the first to come up also if i change the
s=[x,root]
for i in s:
return i
to just
return root
then the time doesn't update itself.
Could anyone help?
Thanks!
Maybe you forgot to type something, most probably your kv is coded wrong. Why is there that hanging MyScreen: and what is it assigned to? You need a main rule somewhere, but I see none. I assigned your MyScreen: to <MyApp>: as a normal return MyScreen() or return some_scrmanager would do and it works.
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
import time
from kivy.uix.label import Label
from kivy.lang import Builder
Builder.load_string('''
<MyApp>:
MyScreen:
<MyScreen>: #
timeb:time_box
BoxLayout:
orientation: "horizontal"
pos_hint: {'top':1}
height: "40dp"
size_hint_y: None
Label:
id:time_box
text:root.update()
size_hint_x: 6
font_size:30
''')
class MyScreen(Screen):
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenManager()
Clock.schedule_interval(x.update, 1)
root.add_widget(MyScreen(name='Screen1'))
s=[x,root]
for i in s:
return i
if __name__ == '__main__':
MyApp().run()
And the same kv, but more reasonable python code would looke like this:
<ScreenMgr>:
MyScreen:
<MyScreen>:
...
class MyScreen(Screen):
def __init__(self, **kw):
super(MyScreen, self).__init__(**kw)
Clock.schedule_interval(self.update, 1)
def update(self, *args):
self.timeb.text = time.asctime()
return time.asctime()
class ScreenMgr(ScreenManager):
pass
class MyApp(App):
def build(self):
x=MyScreen()
root = ScreenMgr()
root.add_widget(MyScreen(name='Screen1'))
return root
if __name__ == '__main__':
MyApp().run()
Because there's no need to call update() from Screen class in the building function and even outside a class where you want to use it presuming it'll run forever(Do you plan to stop time?).

Categories