Button doing on_release action twice in kivy python - python

I'm trying to make an app that can automate an Instagram page, and I have the script for the program ready, but just need to implement it into a GUI. For this, I'm using Kivy. However, I only need to send one request to the API for this. When I click on a button, it triggers the command attached to on_release of the button twice. How can I fix this?
Simplified Python Script:
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.lang import Builder
class StartScreen(Screen):
pass
class WindowManager(ScreenManager):
pass
class BruhApp(MDApp):
def build(self):
self.root = Builder.load_file("bruh.kv")
if __name__ == '__main__':
BruhApp().run()
.kv File:
WindowManager:
StartScreen:
<StartScreen>:
name: "start"
FloatLayout:
MDLabel:
text: "Welcome to the Instagram Page Automator!"
font: "RobotoThin"
font_style: "H5"
halign: "center"
size_hint:0.9,0.7
color: 0,0,0,0.5
pos_hint:{"center_x":0.5,"y":0.5}
MDFillRoundFlatButton:
md_bg_color: 0, 1, 0.6, 1
text:"Continue"
font: "RobotoThin"
size_hint:0.4,0.07
pos_hint:{"center_x":0.5,"y":0.1}
on_release:
print("yes")
This script prints "Yes" twice for me everytime I press the button.

The kivy system automatically loads a kv file for an App if the file is correctly named (See the documentation). Since your kv file is correctly named, it will be loaded automatically. But since you also load it explicitly using the line:
self.root = Builder.load_file("bruh.kv")
the kv file actually gets loaded twice, which causes some things to be instantiated twice. Try replacing the build() method with a simple pass.

Related

Problem in calling function in kivy (CODED WITH HELP OF PYTHON) - CALLING FUNCTION IN .kv FILE

import kivy
from kivy.app import App
from kivy.uix.widget import Widget
#MADE A CLASS FOR MAKING THE GUI
class Main_GUI(App):
pass
def ind(self):
print("hi")
kv = Main_GUI()
kv.run()
The above is the Python code which i have named sample.py.
Below is the Main_GUI.kv
BoxLayout:
orientation: "vertical"
Button:
text: "Hello"
on_press: ind()
Button:
text: "Btn2"
problem is that the function ind is not called up
Please help in rectifying the error.
ERROR
NameError: name 'ind' is not defined
Please Intimate me if any errors. I am new to kivy.
Your ind() method is defined outside of any class. Probably not what you want, but you can access it from within kv by adding:
#: import ind __main__.ind
to the start of your kv file.
A better approach is to just move that method inside your Main_GUI class:
class Main_GUI(App):
def ind(self):
print("hi")
and access it in kv using the app keyword:
Button:
text: "Hello"
on_press: app.ind()
The app refers to your application instance. See the documentation.

Button Text does not Update

>> BACKGROUND :
I want to update/change the text of a Button in the SecondScreen with a press of a Button in the MainScreen. Well I did some research and did what I want, and when I checked in the terminal the text did change. BUUT, the text shown on the SecondScreen did not.
>> THIS IS WHAT I DID :
((Mind you that I'm only using snippets of code for example, I'm going to post the whole code below.))
Button:
text:"PRESS TO CHANGE TEXT"
on_press:
root.funcself()
## on press it goes to it's root and do the "funcself" function in it
which is :
class MainScreen(Screen):
def funcself(self):
app.second.funcscreen()
## it re-directs to the SecondScreen and do the "funcscreen" function
which is :
class SecondScreen(Screen):
def funcscreen(self):
self.ids["button"].text = "SUPPOSED TO CHANGE TO THIS"
and then I checked if I did it successfully by doing print(self.ids["button"].text), and yes!
It did change, but when I navigated to the next screen, the text shown still didn't change.
Anyone mind helping and explaining?
FULL CODE :
python file :
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
def funcself(self):
app.second.funcscreen()
class SecondScreen(Screen):
def funcscreen(self):
value = self.ids["button"]
self.ids["button"].text = "SUPPOSED TO CHANGE TO THIS"
kv = Builder.load_file("reproduce.kv")
class reproduce(App):
second = SecondScreen()
def build(self):
return kv
def change_screen(self, x):
scrnmngr = self.root.ids["sm"]
scrnmngr.current = x
def check(self):
print(self.second.ids["button"].text)
if __name__ == "__main__":
app = reproduce()
app.run()
kivy file :
<MainScreen>:
GridLayout:
rows:2
Label:
text: "PRESS TO GO TO THE NEXT PAGE"
GridLayout:
cols:2
Button:
text:"PRESS TO CHANGE TEXT"
on_press:
root.funcself()
Button:
text:">>>"
on_press:
app.change_screen("second")
root.manager.transition.direction = "left"
<SecondScreen>:
GridLayout:
rows:2
Label:
id:label
text: "PRESS TO CHECK AND RETURN TO PREV PAGE"
Button:
id:button
text:"TEXT BEFORE CHANGE"
on_press:
app.change_screen("first")
root.manager.transition.direction = "right"
app.check()
GridLayout:
cols: 1
ScreenManager:
id:sm
MainScreen:
id:main
name:"first"
SecondScreen:
id:second
name:"second"
Root Cause
It did not change because there are two instances of SecondScreen() i.e. one instantiated in the kv file and the other one instantiated in the App class, reproduce(). The view presented is created from the kv file and the second instance does not has a view associated to it.
Solution
There are two solutions to the problem, and remove second = SecondScreen() from the App class.
Kivy Screen ยป default property manager
Each screen has by default a property manager that gives you the
instance of the ScreenManager used.
Using get_screen()
class MainScreen(Screen):
def funcself(self):
self.manager.get_screen('second').funcscreen()
Using App.get_running_app() & ids
class MainScreen(Screen):
def funcself(self):
App.get_running_app().root.ids.second.funcscreen()
Example
In the following example, there are two solutions provided but one of it is commented off.
main.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
class MainScreen(Screen):
def funcself(self):
self.manager.get_screen('second').funcscreen()
# App.get_running_app().root.ids.second.funcscreen()
class SecondScreen(Screen):
def funcscreen(self):
value = self.ids["button"]
self.ids["button"].text = "SUPPOSED TO CHANGE TO THIS"
kv = Builder.load_file("reproduce.kv")
class reproduce(App):
def build(self):
return kv
def change_screen(self, x):
scrnmngr = self.root.ids["sm"]
scrnmngr.current = x
def check(self):
print(self.second.ids["button"].text)
if __name__ == "__main__":
reproduce().run()
Output
The second attribute you define in your app class, is a new instantiation of the screen, and not really the instance you got in your screenmanager, which you add in kv. This is why when you check, you see its changed, but not on the right instance. And again when you call app.second.func, from mainscreen, again its the wrong instance.
But your app always has a root. In your case its the gridlayout. And every screen has a manager. There are a couple of ways to acces it. But you can do like this.
In your mainscreen class in kv:
Button:
text:"PRESS TO CHANGE TEXT"
on_press:
root.manager.get_screen("second").ids["button"].text = "Something"
Here it gets the screenmanager, and uses its get_screen() method to get the screen named second, and then the id's of that kv rule.

Kivy: How to update an image on a screen?

Hello, I am currently creating a GUI using kivy, on Windows. I am using it to ssh into a Raspberry Pi, take an image, then scp it back to my windows machine. I have successfully done this using the GUI. I have two screens. The first is the login. Once I login a second screen appears with buttons and an image. The login screen and buttons serve their functions properly. However, the image file does not update like I want it to. Either I want it to update in a certain interval, or update after I take a picture.
Here is the
Second Screen Interface. I want the picture on the top right to update itself automatically. The "take picture" button takes the picture then sends it to my computer where I'd like to refresh the image in the GUI and display it
My main python file, which I called "ScreenChange2.py" is shown below
import kivy
import os
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.image import Image
from kivy.properties import StringProperty
from kivy.clock import Clock
from kivy.uix.widget import Widget
from sshtest import ssh #import the ssh class from the ssh python file
class ScreenOne(Screen):
def login(self): #define login using ssh as a function
#Make a Class Variable called connection. This allows for other
#classes to call on it without passing it as an argument
ScreenOne.connection = ssh("192.168.1.3", "pi", "seniordesign")
#ScreenOne.connection.sendCommand("ls")
#ScreenOne.connection.sendCommand("mkdir thisistest")
print("Logging in") #For error checking
def gpioSet(self): #allows for gpio pins to trigger image capture
ScreenOne.connection.sendCommand("echo '18' > /sys/class/gpio/export")
ScreenOne.connection.sendCommand("echo 'out' > /sys/class/gpio/gpio18/direction")
class ScreenTwo(Screen): #This is where all the functions are
def command(self, input): #create a function that sends command through ssh
ScreenOne.connection.sendCommand(input) #Call on connection made before to send command
class MyScreenManager(ScreenManager):
pass
#Create the Application that will change screens. Add App(App) to end of wanted classname
class ScreenChangeApp(App):
#Create a function that builds the app. Keyword self make sures to reference
#current instantiation
def build(self):
screen_manager = ScreenManager()
screen_manager.add_widget(ScreenOne(name = "screen_one"))
screen_manager.add_widget(ScreenTwo(name = "screen_two"))
return screen_manager #after building the screens, return them
#MyScreenManager.login()
sample_app = ScreenChangeApp()
sample_app.run()
The KV file is as shown
#: import os os
<CustomButton#Button>:
font_size: 12
size_hint: .2,.1
<Picture#Image>:
id: image
<ScreenOne>: #define The First Screen
BoxLayout: #Use box layout
Button: #create button
text: "Connect to the Raspberry Pi"
on_press:
root.login()
root.gpioSet()
root.manager.transition.direction= "left"
root.manager.transition.duration = 1
root.manager.current = "screen_two"
<ScreenTwo>:
BoxLayout:
spacing: 10
padding: 10
orientation: "vertical"
CustomButton:
text: "Send an ls command"
on_press:
root.command("ls")
CustomButton:
text: "Take picture"
on_press:
root.command("python cameradaemon.py &") #'&' runs script in background
root.command("sleep .1")
root.command("echo '1' > /sys/class/gpio/gpio18/value") #set pin high to take pic
root.command("echo '0' > /sys/class/gpio/gpio18/value") #take it off to prevent another photo
root.command("scp TEST.jpg Jason#192.168.1.10:C:/Users/Jason/Desktop/RaspberryPiTransferred")
root.command("kill %1")
CustomButton:
text: "Create Histogram"
on_press:
os.system("cd C:/Users/Jason/Desktop/KivyFiles/Histogram & python histogram.py")
AnchorLayout:
anchor_x: 'right'
anchor_y: 'top'
padding: 10
BoxLayout
size_hint: .3, .3
Image:
source: 'C:/Users/Jason/Desktop/RaspberryPiTransferred/TEST.jpg'
As you can probably tell, at the end of the KV file, I insert the image file in a box layout as static. I understand this is why my image won't update, but what I need to know is how I can update it automatically.
I was thinking I could maybe make a custom picture rule, which I started at the top as
<Picture#Image>:
I also understand there is a Reload() function for image files, but I do not understand how to implement that in a KV file.
I've tried creating a class in my main file that runs the reload function every second, but the image doesn't display as it isn't linked to any image in the KV file.
In other words, how do I make it so that the image being displayed in this Second Screen to update automatically given the two scripts I've given. Thank you
In your ScreenTwo class create a StringProperty at the class level:
class ScreenTwo(Screen): #This is where all the functions are
img_src = StringProperty('C:/Users/Jason/Desktop/RaspberryPiTransferred/TEST.jpg')
def command(self, input):
And in your kv file reference it by:
Image:
source: root.img_src
Then change the image just by doing:
img_src = 'some/new/image.jpg'
The image should update without any further actions needed.

How can I bind a button on the .kv file to make it play a sound?

Hello StackOverflow community,
I would like to ask for some help as before asking I did a long research but found nothing to help me out.
I have a school project that I decided to code with Python using Kivy for cross-platform. My project is about a SoundBox, to simplify I need to first create buttons and bind them to play various sounds. On pure python code (without a .kv file), I learned how to bind a button to make it play a sound, so I decided to reach the next level that is the Screen Management part. I kind of learned that to using now a .kv file to make it simple but I'm stuck on how to bind a button using .kv file.
I tried out some stuff but always ended up with errors on the console, also (but it's not really important for now), my Fade Transition doesn't work.
Your help is highly appreciated, thanks in advance.
.py:
from kivy.app import App
from kivy.uix.button import Button
from kivy.core.audio import SoundLoader
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
sound = SoundLoader.load('Sunset_Lover.ogg')
sm = ScreenManager()
class ScreenManager(ScreenManager):
pass
class Menu(Screen):
pass
class Genre(Screen):
pass
class TestApp(App):
def build(self):
sm.add_widget(Menu(name='menu'))
sm.add_widget(Genre(name='genre'))
return sm
def son(self, instance):
if sound:
sound.play()
if __name__ == "__main__":
TestApp().run()
.kv:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
<ScreenManager>:
FadeTransition:
Menu:
Genre:
<Menu>:
BoxLayout:
Button:
text: "Commencer"
size_hint: 1, 1
pos_hint: {'x': 0.3, 'y':0.3}
on_press: root.manager.current = 'genre'
<Genre>:
BoxLayout:
Button:
text: "Exemple1"
size_hint: 0.2, 0.2
pos_hint: {'x': 0.2, 'y':0.2}
on_press: root.son()
The problem is that you have sound out of any scope you could use in kv file. First, move it somewhere, where you can access it:
class TestApp(App):
def build(self):
self.sound = SoundLoader.load('file')
sm = ScreenManager()
sm.add_widget(Menu(name='menu'))
sm.add_widget(Genre(name='genre'))
return sm
Then collect the arguments in more efficient way - this way you can use it both in kv and python and the additional args will be collected (won't throw an error)
def son(self, *args):
if self.sound:
self.sound.play()
Then in kv you have to make sure the ScreenManager recieves only the appropriate widgets i.e. Screens only. To get the transition working, you have to add it to a variable it's used from:
<ScreenManager>:
transition: FadeTransition()
Menu:
Genre:
And to actually play the sound (run the method) you can call it from the place you define it in i.e. from App instance:
<Genre>:
BoxLayout:
Button:
text: "Exemple1"
size_hint: 0.2, 0.2
pos_hint: {'x': 0.2, 'y':0.2}
on_press: app.son() # here
You can import it to the kv file:
#: import sound __main__.sound
Button:
on_release: sound()

New instance of Kivy widget does not have attributes of kv lang file

I created a class for a Popup and set the title property in the kv file.
When the popup shows, it does not have the title as in the kv file, but instead shows No Title as if it was never set.
It is exactly the same problem as here, but I do not understand from this link what the problem is or how to make it work:
https://github.com/kivy/kivy/issues/751
I understand how to do this using IDs in kv lang, but that only works if the Popup is put as a child widget of the root widget (ex. MainUI). Then I can link an instance of a python class to a widget in the kv file.
But then the popup displays as part of the root widget.
What I want to do, is instantiate a new instance of the popNewDB class when the New button is clicked and have this instance use the values such as "title" in the KV file.
Can you please explain how to do this?
Here is my code:
py file:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
class popNewDB(Popup):
pass
class MainUI(Widget):
pop = ObjectProperty(popNewDB())
def showpopup(self):
self.pop.open()
class VerseReminder(App):
def build(self):
return MainUI()
if __name__ == '__main__':
VerseReminder().run()
kv file:
#:kivy 1.9.1
<popNewDB>
title: 'Hallo'
<MainUI>
Label:
pos: root.center_x - self.width/2,root.center_y + 200
text: "Quote Reminder"
BoxLayout:
size_hint: None,None
width: 400
height: 200
pos: root.center_x-200,root.center_y-50
orientation: 'vertical'
spacing: 20
Button:
size_hint: 1,1
text: "New..."
on_press: root.showpopup()
Button:
size_hint: 1,1
text: "Open..."
Button:
size_hint: 1,1
text: "Quit"
At the time pop = ObjectProperty(popNewDB()) is evaluated, the rules haven't been loaded, so only a barebones Popup will be created. Instead, you could do this:
class MainUI(Widget):
pop = ObjectProperty()
def showpopup(self):
if self.pop is None:
self.pop = PopNewDB()
self.pop.open()
Here, the first time the button is pressed, a new instance of PopNewDB will be created and stored in self.pop.
(NB: I renamed the Popup subclass to start with a Capital Letter, to be consistent with language standards and kivy expectations)

Categories