How to add Double tap to images in KivyMD? - python

The double tap works on the first code that uses a .kv file however I don't know how to get it to work on a code that doesn't use a .kv file. The second code below is generating MDCards as posts with a title, image, and subtitle. I want to add double-tap to that image only. How can I achieve this?
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.behaviors import TouchBehavior
from kivymd.uix.button import MDRaisedButton
KV = '''
Screen:
MyButton:
text: "PRESS ME"
pos_hint: {"center_x": .5, "center_y": .5}
'''
class MyButton(MDRaisedButton, TouchBehavior):
def on_double_tap(self, *args):
print("<on_double_tap> event")
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
MainApp().run()
How can I add a double-tap to the image within the MDCard?
from kivy.core.window import Window
from kivy.uix.scrollview import ScrollView
from kivymd.uix.card import MDCard
from kivymd.uix.gridlayout import MDGridLayout
from kivymd.app import MDApp
from kivymd.uix.behaviors import TouchBehavior
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.label import MDLabel
from KivaMD.kivymd.utils.fitimage import FitImage
Window.size = (440, 760)
class MyButton(MDRaisedButton, TouchBehavior):
def on_double_tap(self, *args):
print("<on_double_tap> event")
class MainApp(MDApp):
def build(self):
blog = MDGridLayout(cols=1, spacing=40, size_hint_y=None, padding=[20,], md_bg_color=[0,0,0.1,.2])
blog.bind(minimum_height=blog.setter('height'))
for i in range(10):
post = MDCard(size_hint=(.9, None), size=(300, 300), radius=10)
container = MDGridLayout(cols=1)
post.add_widget(container)
container.add_widget(MDLabel(text="Title", halign='center'))
#ADD DOUBLE TAP TO THIS IMAGE
container.add_widget(FitImage(source="data/assets/img/placeholder.jpg", size_hint_y=None, height=200))
container.add_widget(MDLabel(text="Subtitle", halign='center'))
blog.add_widget(post)
scroll = ScrollView(size_hint=(1, None), size=(Window.width, Window.height))
scroll.add_widget(blog)
return scroll
MainApp().run()

Do it just as your show for the MyButton class:
class MyFitImage(FitImage, TouchBehavior):
def on_double_tap(self, *args):
print("MyFitImage: <on_double_tap> event")
Then use MyFitImage in place of FitImage in your code.

Related

I can't replace a Widget with a Screen in kivy

I'm a beginner at kivy, I have an fclass(Widget) that I want it to be a fclass(Screen), but when I tried to make the change all the screen messed up, the code generate some buttons with a for loop, I wish I coud do the same with a float layout, but I want the fclass to stay a screen since I'm building a multiscreen app.
Here is the .py file:
import kivy
from kivy.uix.gridlayout import GridLayout
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.textinput import TextInput
from kivy.clock import Clock
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.core.window import Window
from kivy.config import Config
from kivy.lang import Builder
from kivy.properties import StringProperty, ObjectProperty, NumericProperty,ReferenceListProperty
from kivy.graphics.texture import Texture
from kivy.core.camera import Camera
from kivy.graphics import *
import time
import os
from pathlib import Path
#import cv2
import struct
import threading
import pickle
Builder.load_file('the.kv')
class fscreen(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.list_of_btns = []
def create(self):
self.h = self.height*0.9
for i in range(4):
self.h = self.h - self.height*0.1
self.btn = Button(text='button '+str(i), size=(self.width*0.4,self.height*0.05), pos=(self.width*0.3, self.h), on_press=self.press)
self.list_of_btns.append(self.btn)
self.add_widget(self.btn)
def press(self, instance):
print(instance.text)
def delete(self):
for btns in self.list_of_btns:
self.remove_widget(btns)
class theapp(App):
def build(self):
self.screenm = ScreenManager()
self.fscreen = fscreen()
screen = Screen(name = "first screen")
screen.add_widget(self.fscreen)
self.screenm.add_widget(screen)
return self.screenm
if __name__ == "__main__":
theapp = theapp() #
theapp.run()
The .kv file:
<fscreen>
Button:
text: 'create'
size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.1
on_press: root.create()
Button:
text: 'delete'
size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.2
on_press: root.delete()
How can I make the fclass a screen class without messing up everything ?
Thank you in advance
Seems to me that code should work, but it doesn't. A fix is to use size_hint instead of size in both your kv and py. So the kv could look like:
<fscreen>:
Button:
text: 'create'
size_hint: 0.4, 0.05
# size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.1
on_press: root.create()
Button:
text: 'delete'
size_hint: 0.4, 0.05
# size: root.width*0.4, root.height*0.05
pos: root.width*0.3, root.height*0.2
on_press: root.delete()
and in the create() method:
def create(self):
self.h = self.height * 0.9
for i in range(4):
self.h = self.h - self.height * 0.1
self.btn = Button(text='button ' + str(i), size_hint=(0.4, 0.05),
pos=(self.width * 0.3, self.h), on_press=self.press)
self.list_of_btns.append(self.btn)
self.add_widget(self.btn)
Your size in the kv and py were both just trying to do the size_hint anyway.
And, of course, your build() method must be adjusted:
def build(self):
self.screenm = ScreenManager()
self.fscreen = fscreen(name="first screen")
self.screenm.add_widget(self.fscreen)
return self.screenm
Other things to note:
You should use upper case for class names. Failure to do so can lead to errors in kv
You should consider using pos_hint instead of pos to allow better resizing of your App

Outputting the input text in a new screen

Basically i'm trying to take some input from a user, perform some actions, and then output the result in a new screen (i'm thinking as the label of the new screen). I've managed to switch between screens but i cannot figure out how to output the input from the first screen to the second screen. I've tried to make the input data a global variable so i can assign to the text of the label of the output screen, but it didn't work. Here's my python file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen,ScreenManager
from kivy.core.window import Window
from kivymd.app import MDApp
from kivy.uix.image import Image
from kivy.animation import Animation
from kivy.clock import Clock
from kivy.properties import ColorProperty
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from plyer import filechooser
from kivymd.uix.dialog import MDDialog
Window.clearcolor = (1,1,1,1)
g_data = 'xstring'
class MainWindow(Screen):
def get_data(self):
global g_data
g_data = self.ids.user_input.text
class OutputScreen(Screen):
def ex(self):
self.ids.output_label.text = g_data
class mainApp(MDApp):
def __init__(self):
super().__init__()
def change_screen(self):
screemanager = self.root.ids['screenmanager']
screemanager.current = 'output'
def change(self):
self.change_screen()
if __name__ == '__main__':
mainApp().run()
and my kv file:
#:import utils kivy.utils
GridLayout:
cols:1
ScreenManager:
id: screenmanager
MainWindow:
id: main
name: 'main'
OutputScreen:
id: output
name: 'output'
<MainWindow>:
FloatLayout:
TextInput:
id: user_input
pos_hint:{"x" : 0.05, "top" : 0.9}
size_hint: 0.9, 0.37
Button:
pos_hint:{"top" : 0.51, "x" : 0.05}
size_hint: (None,None)
width : 150
height : 40
font_size : 23
text:'Submit'
on_release: app.change()
<OutputScreen>:
FloatLayout:
Label:
id: output_label
text: root.ex()
color: 0,0,0,1
Thank you very much.
I can't get your example to work, but you would need to do this:
g_data = '' # new global variable defined
class MainWindow(Screen):
def get_data(self):
global g_data # global goes here
g_data = self.ids.user_input.text
class OutputScreen(Screen):
def ex(self):
self.ids.output_label.text = g_data

Why can't I dismiss kivy popup?

I created an application where, on start, a pop-up window appears asking for login credentials.
After providing the right credentials, this pop-up should close, so the "main window" behind it is accessible.
main.py:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.graphics import Rectangle
from kivy.graphics import Color
from kivy.graphics import Line
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.config import Config
from kivy.uix.popup import Popup
from kivy.clock import Clock
from kivy.core.window import Window
Config.set('graphics', 'width', '1024')
Config.set('graphics', 'height', '768')
class LoginWindow(Screen):
pass
class MainWindow(Screen):
pass
class WindowManager(ScreenManager):
pass
class LoginPopup(Screen): # Popup Window
def login_popup(dt): # Function to call Popup Window
show = LoginPopup()
popupWindow = Popup(title="Please log in", content=show, size_hint=(None, None), size=(400, 125),
auto_dismiss=False)
popupWindow.open()
kv = Builder.load_file("my.kv")
class MainApp(App):
def dismiss(self):
self.dismiss()
def build(self):
Clock.schedule_once(LoginPopup.login_popup, 1) # Loading the login popup 1 second after initialising
return kv
if __name__ == "__main__":
MainApp().run()
my.kv
<LoginPopup>:
id: popupWindow
GridLayout:
rows: 2
FloatLayout:
size_hint: 1,0.5
rows: 1
cols: 2
Label:
pos: (0,40)
text: "Password: "
text_size: self.size
TextInput:
pos: (80,35)
size_hint_y: (.8)
size_hint_x: (.785)
password: True
id: password
multiline: False
Button:
id: login_button
text: "Login"
size_hint: 1,0.5
pos_hint: {"x":0,"y":0.1}
on_release:
root.login_popup.popupWindow.dismiss() if password.text == "XXX" else None
print(password.text)
Inside the my.kv I want to dismiss the pop-up with root.login_popup.popupWindow.dismiss() if password.text == "XXX" else None but I get an error that "'function' object has no attribute 'popupWindow'
Is this because the "popupWindow" object is instantiated with another name? How can I fix this?
(I took out some of the my.kv code of other screens as they are not in use.)
The popupWindow variable in your code is a local variable to the login_popup() method. You can make that a class level variable by changing your LoginPopup class to:
class LoginPopup(Screen): # Popup Window
popupWindow = None
def login_popup(dt): # Function to call Popup Window
show = LoginPopup()
LoginPopup.popupWindow = Popup(title="Please log in", content=show, size_hint=(None, None), size=(400, 125),
auto_dismiss=False)
LoginPopup.popupWindow.open()
Then you can access it in your kv as:
on_release:
root.popupWindow.dismiss() if password.text == "XXX" else None
print(password.text)

Insert a graph with kv lang

I am trying to make an APP with 2 screen:
First screen is a Button
Second screen shows a graph
When the Button of the first screen is pressed, the second screen appears with the graph.
I was able to plot the graph with 1 screen only, using matplotlib.
Here is my code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("module://kivy.garden.matplotlib.backend_kivy")
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.uix.widget import Widget
class Sensores(Screen):
pass
class Grafico(Screen):
def build(self):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
return box
class Menu(ScreenManager):
pass
presentation = Builder.load_file('sensor.kv')
class sensor(App):
def build(self):
return presentation
if __name__ == "__main__":
sensor().run()
KIVY
Menu:
Sensores:
Grafico:
<Sensores>
name: 'sensores'
BoxLayout:
Button:
text: "Sensor 01"
on_release:
root.Grafico()
<Grafico>
name: 'grafico'
I expect to have the graph in the second screen.
I see two problems with your code. First, in your kv file the Button action is incorrect:
Button:
text: "Sensor 01"
on_release:
root.Grafico()
If the Button is intended to switch to the other screen, it should be:
Button:
text: "Sensor 01"
on_release:
root.manager.current='grafico'
Second, in your Grafico class you have a build() method that is never called. If you change that from:
class Grafico(Screen):
def build(self):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
return box
to:
class Grafico(Screen):
def on_enter(self, *args):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
self.add_widget(box)
I think you will get the desired result. The key is that the on_enter() method is called when the Grafico Screen is displayed. The method is your code, but with a self.add_widget(box) added in order to add the box to the screen. See the Screen Documentation for more info.
Thank you very much!
It works now! Follow the code:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("module://kivy.garden.matplotlib.backend_kivy")
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.uix.widget import Widget
plt.plot([1,23,2,4])
plt.ylabel("alguns numeros legais")
class Sensores(Screen):
pass
class Grafico(Screen):
def on_enter(self, *args):
box = BoxLayout()
box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
self.add_widget(box)
class Menu(ScreenManager):
pass
presentation = Builder.load_file('sensor.kv')
class sensor(App):
def build(self):
return presentation
if __name__ == "__main__":
sensor().run()
KV LANG
#:kivy 1.9.1
Menu:
Sensores:
Grafico:
name: 'grafico'
<Sensores>
name: 'sensores'
BoxLayout:
Button:
text: "Sensor 01"
on_release:
root.manager.current = 'grafico'
<Grafico>
name: 'grafico'

Kivy: pass data to another class

I`m trying to make a simple GUI with Kivy(1.9) using a popup to change some options from a list and save it to a db, for example. When i call popup(), Python(3.4.5) crash..
main.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.properties import ListProperty
from kivy.lang import Builder
Builder.load_string('''
<PopView>:
title: 'Popup'
size_hint: (.8, .8)
Button:
text: 'Save'
''')
class MainApp(App):
def build(self):
b = Button(text='click to open popup')
b.bind(on_click=self.view_popup())
return b
def view_popup(self):
a=PopView()
a.data=[1,2,3,4] #e.g.
a.open()
class PopView(Popup):
def __init__(self):
self.data = ListProperty()
def save_data(self):
#db.query(self.data)
pass
if __name__ in ('__main__', '__android__'):
MainApp().run()
Here are a couple of things.
First, if you are going to overite __init__ remember to call super
But in this simple case you dont need __init__
Then, there is no on_click event on Button. Use on_press or on_release
And last but not least: You dont need to call the method in the bind function. Only pass it (without ())
So now your example looks like this.
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.properties import ListProperty
from kivy.lang import Builder
Builder.load_string('''
<PopView>:
title: 'Popup'
size_hint: (.8, .8)
Button:
text: 'Save'
''')
class MainApp(App):
def build(self):
b = Button(text='click to open popup')
b.bind(on_release=self.view_popup)
return b
def view_popup(self,*args):
a = PopView()
a.data=[1,2,3,4] #e.g.
a.open()
class PopView(Popup):
data = ListProperty()
def save_data(self):
#db.query(self.data)
pass
if __name__ in ('__main__', '__android__'):
MainApp().run()

Categories

Resources