Display image from different funtion in kivy python - python

Hi im trying to build a scanner which takes an image and on clicking submit button it should return resultant image in new screen, this is where im till now, help is much appreciated. Thanks in advance
from kivy.app import App
from kivy.lang import Builder
import time
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
Builder.load_string( '''
<CameraClick>:
orientation: 'vertical'
Camera:
id: camera
resolution: 500,500
BoxLayout:
orientation: 'horizontal'
size_hint_y: None
height: '48dp'
Button:
text: 'Click'
on_press: root.capture()
on_release: camera.play = False
Button:
text: 'Submit'
on_press: root.capture()
on_release: camera.play = False
''')
class CameraClick(BoxLayout):
def capture(self):
'''
Function to capture the images and give them the names
according to their captured time and date.
'''
camera = self.ids['camera']
print("camera down")
print(type(camera))
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
return Image(source='hey.png')
# def release(self):
class CameraApp(App):
def build(self):
return CameraClick()
if __name__ == '__main__':
CameraApp().run()

When you click button then Kivy runs assigned function but it doesn't get returned value - it wouldn't know what to do with returned value.
You have to assign value to global variable or to class (with self.) and other function has to get value from this variable.
def capture(self):
self.image = Image(source='hey.png')
def other_function(self):
do_something(self.image)
If other_function can be executed before capture then it is good to create this variable at start with some default value - ie. None
class CameraClick(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.image = None
and then you can check if capture was executed
def other_function(self):
if self.image:
do_something(self.image)
else:
print('Image not captured yet')
If you want to use the value in different classes then you may have to assign it to global variable - eventually if it possiblem you can send instance of one class to other class as arguments
camera_click = CameraClick()
OtherClass(camera_click)
and other class can keep it and later it can use it to get image
class OtherClass():
def __init__(self, camera, **kwargs):
super().__init__(**kwargs)
self.camera = camera
def some_function(self):
if self.camera.image:
do_something(self.camera.image)
else:
print('Image not captured yet')
BTW: Sometimes classes may have the same parent and then OtherClass can use
self.parent.camera_click.image

Related

how can i add text to my popup label from another class function with kivy

I want to display the color name that i found at { closest_colour(requested_colour) } function
in popup window.
In practice application supposed to ask for file from your computer (only specific image types) then ask for mouse input while displaying image that you choose.Finally opens popup window to display color of pixel that you clicked.
.py
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.popup import Popup
import cv2
import webcolors
class LoadDialog(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
class Root(FloatLayout):
loadfile = ObjectProperty(None)
def dismiss_popup(self):
self._popup.dismiss()
def show_load(self):
content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content,
size_hint=(0.9, 0.9))
self._popup.open()
def load (self,path, filename):
filename=str(filename)
filename = filename.replace("[", "")
filename = filename.replace("]", "")
filename = filename.replace("\\\\", "\\")
filename = filename.replace("'", "")
print(filename)
img=cv2.imread(filename)
cv2.imshow('image', img)
self.dismiss_popup()
def closest_colour(requested_colour):
min_colours = {}
for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
b_c, g_c, r_c = webcolors.hex_to_rgb(key)
rd = (r_c - requested_colour[0]) ** 2
gd = (g_c - requested_colour[1]) ** 2
bd = (b_c - requested_colour[2]) ** 2
min_colours[(rd + gd + bd)] = name
return min_colours[min(min_colours.keys())]
def click_event(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN:
print(x, ' ', y)
requested_colour=img[y,x]
global answer
answer=closest_colour(requested_colour)
print(closest_colour(requested_colour))
print(img[y,x])
cv2.destroyAllWindows()
cv2.setMouseCallback('image', click_event)
cv2.waitKey(0)
return answer
class MyPopup(Popup):
PopUp=ObjectProperty(None)
text = StringProperty(Root.answer)
def MyPopup(self):
content = MyPopup(PopUp=self.PopUp)
self._popup = Popup(title="Color Found!!", content=content, size_hint=(0.2, 0.4))
self._popup.open()
class main(App):
pass
Factory.register('Root', cls=Root)
Factory.register('LoadDialog', cls=LoadDialog)
Factory.register('MyPopup', cls=MyPopup)
if __name__ == '__main__':
main().run()
.kv
#:kivy 1.1.0
#:import Factory kivy.factory.Factory
Root:
FloatLayout:
Button:
id: load
size_hint:(1,.1)
pos_hint:{'x':0, 'y':0}
text: 'Load'
on_release: root.show_load()
background_color :(0, 0, 1, 1)
Image:
id: img
size_hint:(1,.9)
pos_hint:{'x':0, 'y':.1}
source: 'palet.jpg'
size: self.texture_size
keep_ratio: False
allow_stretch: True
<LoadDialog>:
id:load_dialog
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserIconView:
id: filechooser
filters:['*.png','*.jpg','*.jpeg']
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)
on_release: Factory.MyPopup().open()
<MyPopup#Popup>
auto_dismiss: False
size_hint:0.4,0.2
pos_hint:{"x":0.3,"y":0.4}
title:"Color Found!!"
BoxLayout:
Label:
text:root.text
font_size:24
Button:
text:"Ok"
width:.5
size_hint:0.2,0.2
pos_hint:{"x":0.2,"y":0}
on_release: root.dismiss()
I tried to create global answer variable but it didnt work out.
Okay, the structure of what you're trying to do is a little confusing, the class LoadDialog(FloatLayout) looks like it's being called just to make the connection between the two classes, is that it?
But from the looks of it, you are trying to pass the text from variable
answer
that is returned when you call the function def load(). if so, try creating a variable eg text_answer = None in class Root()
then, before returning the result, pass the variable's value to the new variable created within the class, in the code snippet
cv2.setMouseCallback('image', click_event)
cv2.waitKey(0)
# add this
self.text_answer = answer
# don't need the return
return answer
later in class MyPopup(Popup), add the variable that was created in the Root() class. But remember that, for object-oriented concepts, you need to use the variable from the instance of class Root() and not directly from the class, and that's the tricky part.
There are many ways to do this:
Pass the Root() class instance to the Popup constructor.
Use an ObjectProperty to use the Root instance.
Use kivy's own tree of objects, doing self.parent.parent.[...] until
finding the instance of Root()
For this case, the easiest way is to use the self.parent method, until you find the Root instance and retrieve the text value, try doing something like this:
class MyPopup(Popup):
PopUp=ObjectProperty(None)
# go looking for the instance of Root in the parentage
text = StringProperty(self.parent.parent.text_answer)
I can't give the answer to your specific problem because I don't have some of the libraries you are using, but I believe this should help with communication between classes.
Always remember to try to reference the instances of the classes, not the classes themselves.

Python, Kivy: Problem with calling functions from different classes/screens

I'm having trouble with correctly calling functions from different classes.
I am making a simple game which calculates the score using the amount of time it takes to clear a level. There's a stopwatch running in the background and I want to add a pause button that popup menu, and a resume button inside this popup menu.
The problem is that when calling the pause function from within the popup menu, it will also be returned inside the popup, instead of inside the main widget.
Here is a simplified version of the code:
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.widget import Widget
from kivy.uix.popup import Popup
from kivy.clock import Clock
root_widget = Builder.load_file('app.kv')
class ExampleWidget(Widget):
time = NumericProperty(0)
paused = False
stop = False
# Keeping time
def increment_time(self, interval):
self.time += .1
print(self.time) # To check if stopwatch is running or not
# Stop should mean that the stopwatch must reset when it starts again.
# When paused it should resume when it starts again
def stop_start_or_pause(self):
# stop stopwatch
if self.stop:
Clock.unschedule(self.increment_time)
print('Stopped')
# Make sure time is 0 when restarting
elif not self.stop and not self.paused:
# Keeping time
self.time = 0
Clock.schedule_interval(self.increment_time, .1)
# Pause stopwatch
elif self.paused:
Clock.unschedule(self.increment_time)
print("!!", self.time) # To make it easier to see if stopwatch actually resumes where it left off
print('unscheduled') # Just to confirm and to make it a bit easier to see
# resume stopwatch
elif not self.paused:
Clock.schedule_interval(self.increment_time, .1)
class PopupMenu(Popup):
example = ExampleWidget()
class MyApp(App):
ExampleWidget = ExampleWidget()
def build(self):
return ExampleWidget()
MyApp().run()
.kv file:
#:import Factory kivy.factory.Factory
<PopupMenu#Popup>
auto_dismiss: False
size_hint_y: .8
size_hint_x: .9
title: 'Pause'
example: app.ExampleWidget
BoxLayout:
Button:
text: 'resume'
on_press: root.example.paused = False
on_release: root.dismiss(); root.example.stop_start_or_pause()
size: self.size
<ExampleWidget>:
GridLayout:
col: 2
rows: 3
size: root.size
Button:
text: 'start'
size: self.size
on_press: root.stop = False; root.stop_start_or_pause()
Button:
text: 'stop'
size: self.size
on_press: root.stop = True; root.stop_start_or_pause()
Button:
text: 'Pause menu'
size: self.size
on_press: root.paused = True
on_release: Factory.PopupMenu().open(); root.stop_start_or_pause()
Label:
text: str(round(root.time))
size: self.size
I tried making a function and using Clock.schedule.interval() to keep checking if paused == True, but it keeps returning:
AttributeError: 'float' object has no attribute 'stopped'
This didn't seem like efficient solution anyways, so I didn't want to spend too much time on this function. I also tried to find 'stupid' mistakes (I.e. ',' instead of '.') but that was before I realised that the resume button returned a 'second' stopwatch instead of updating the one I actually wanted to use.
I hope that someone can help, and that my question is clear. English is not my first language so I sometimes have a hard time finding the best way to explain/ask questions.
Thank you in advance!
If I understand your question, the problem is with your MyApp class:
class MyApp(App):
ExampleWidget = ExampleWidget()
def build(self):
return ExampleWidget()
This code is creating two instances of ExampleWidget. One is returned in the build() method, and one is saved as the ExampleWidget attribute of MyApp. Now, when you use the ExampleWidget attribute of MyApp, you are not referencing the ExampleWidget that is the root of your GUI, so it has no effect on what appears on the screen. The fix is to just creat a single instance of ExampleWidget, like this:
class MyApp(App):
ExampleWidget = ExampleWidget()
def build(self):
return self.ExampleWidget

usage of DragBehavior in Kivy

I'm new for python and kivy, and struggling with them.
I wanted to implement drag and drop function (a function that when a image is dragged to another image and dropped on it, do something.) with kivy.
What I want to do are the following three things;
know the id of the element that the user has been holding now.
know the id of the element that the user is on hover.
know the id of the element which was under the pointer when the user has dropped what he had.
I thought this would be useful to deal with them, so I wrote down the python script and .kivy file following. However, it dosen't work as I intended.
there are many problems which I don't know how to solve;
print(self.id) returns None.
Even though I am dragging only 1 element, on_touch_up prints None twice.
once you dragged and dropped a image, the image cannot be dragged anymore.
python file:
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.behaviors import DragBehavior
from kivy.properties import BooleanProperty
from kivy.properties import ObjectProperty
from kivy.factory import Factory
from kivy.core.window import Window
class HoverBehavior(object):
"""Hover behavior.
:Events:
`on_enter`
Fired when mouse enter the bbox of the widget.
`on_leave`
Fired when the mouse exit the widget
"""
hovered = BooleanProperty(False)
border_point = ObjectProperty(None)
'''Contains the last relevant point received by the Hoverable. This can
be used in `on_enter` or `on_leave` in order to know where was dispatched the event.
'''
def __init__(self, **kwargs):
self.register_event_type('on_enter')
self.register_event_type('on_leave')
Window.bind(mouse_pos=self.on_mouse_pos)
super(HoverBehavior, self).__init__(**kwargs)
def on_mouse_pos(self, *args):
if not self.get_root_window():
return # do proceed if I'm not displayed <=> If have no parent
pos = args[1]
# Next line to_widget allow to compensate for relative layout
inside = self.collide_point(*self.to_widget(*pos))
if self.hovered == inside:
# We have already done what was needed
return
self.border_point = pos
self.hovered = inside
if inside:
self.dispatch('on_enter')
else:
self.dispatch('on_leave')
def on_enter(self):
pass
def on_leave(self):
pass
Factory.register('HoverBehavior', HoverBehavior)
class DraggableImage(DragBehavior, HoverBehavior, Image):
def __init__(self, **args):
super(DraggableImage, self).__init__(**args)
self.source_file = ""
self.is_on_hover = False
def on_enter(self):
self.source_file = self.source
self.source = "green.png"
self.is_on_hover = True
print(self.id)
def on_leave(self):
self.source = self.source_file
self.is_on_hover = False
print(self.id)
def on_touch_up(self, touch):
if self.is_on_hover:
print(self.id)
class TestApp(App):
def build(self):
pass
if __name__ == '__main__':
TestApp().run()
test.kivy:
<DraggableImage>
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
BoxLayout:
orientation: "horizontal"
DraggableImage:
id: "left_left"
source: "red.png"
DraggableImage:
id: "left_right"
source: "yellow.png"
DraggableImage:
id: "right_left"
source: "red.png"
DraggableImage:
id: "right_right"
source: "yellow.png"
Create a StringProperty named, name for example. Don't use id, because id is used in kv.
Change your class and kv as so:
from kivy.properties import StringProperty
class DraggableImage(DragBehavior, HoverBehavior, Image):
name = StringProperty("")
def __init__(self, **args):
super(DraggableImage, self).__init__(**args)
self.source_file = ""
self.is_on_hover = False
def on_enter(self):
self.source_file = self.source
self.source = "green.png"
self.is_on_hover = True
print(self.name)
def on_leave(self):
self.source = self.source_file
self.is_on_hover = False
print(self.name)
def on_touch_up(self, touch):
if self.is_on_hover:
print(self.name)
KV = """
<DraggableImage>:
drag_rectangle: self.x, self.y, self.width, self.height
drag_timeout: 10000000
drag_distance: 0
BoxLayout:
orientation: "horizontal"
DraggableImage:
name: "left_left"
source: "red.png"
DraggableImage:
name: "left_right"
source: "yellow.png"
DraggableImage:
name: "right_left"
source: "red.png"
DraggableImage:
name: "right_right"
source: "yellow.png"
"""

Kivy - Update a label with sensor data?

New to kivy, and OOP.
I'm trying to update a label in kivy with data I pull from a temp sensor. The code that pulls in the sensor data is in labeltempmod. I created a function getTheTemp() that is called every second. In the function I try to assign the text of the label via Label(text=(format(thetemp)), font_size=80). The program ignores this. What am I doing wrong here?
#This is a test to see if I can write the temp to label
import labeltempmod
import kivy
from kivy.app import App
from kivy.clock import Clock
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.boxlayout import BoxLayout
def getTheTemp(dt):
thetemp = labeltempmod.readtemp()
Label(text=(format(thetemp)), font_size=80)
print thetemp
class LabelWidget(BoxLayout):
pass
class labeltestApp(App):
def build(self):
# call get_temp 0.5 seconds
Clock.schedule_interval(getTheTemp, 1)
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Here is the kivy language file:
<LabelWidget>:
orientation: 'vertical'
TextInput:
id: my_textinput
font_size: 80
size_hint_y: None
height: 100
text: 'default'
FloatLayout:
Label:
id: TempLabel
font_size: 150
text: 'Temp Test'
Thanks.
Sorry but you never update something You are just creating another label
Try this:
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
thetemp = labeltempmod.readtemp()
self.ids.TempLabel.text = thetemp
print thetemp
class labeltestApp(App):
def build(self):
return LabelWidget()
if __name__ == "__main__":
labeltestApp().run()
Update : for your last request, I think the best way to do that is:
...
class LabelWidget(BoxLayout):
def __init__(self, **kwargs):
super(LabelWidget, self).__init__(**kwargs)
self.Thetemp = None
Clock.schedule_interval(self.getTheTemp, 1)
def getTheTemp(self, dt):
if self.Thetemp is None:
self.thetemp = labeltempmod.readtemp()
else:
self.thetemp = labeltempmod.readtemp(self.theTemp)
self.ids.TempLabel.text = str(self.thetemp)

(Kivy Python) Switching Screens on Button Press Inside .py file

I understand that it is relatively easy to switch screens in the .kv file using on_release. I want to keep my button creating in the .py file, however, so I do not want to use this method. I have done the following to add a function that occurs when the 14th button is pressed. When the button is pressed in the program nothing happens. Experimenting with other names for the screen to sm.current to threw the error: "kivy.uix.screenmanager.ScreenManagerException: No Screen with name "InputScreen" when the 14th button was pressed."
# Kivy Formatting
kv_text='''\
<MyScreenManager>:
LandingScreen:
InputScreen:
<InputScreen#Screen>:
name: 'input_sc'
AnchorLayout:
id: anchor_1
<LandingScreen#Screen>:
name: 'landing_sc'
GridLayout:
id: grid_1
cols: 5
height: 480
width: 800
spacing: 25, 20
padding: 25,25
'''
# Screen Manager
class MyScreenManager(ScreenManager):
pass
# Main screen with button layout
class LandingScreen(Screen):
def __init__(self, **kwargs):
super(LandingScreen, self).__init__(**kwargs)
self.buttons = [] # add references to all buttons here
Clock.schedule_once(self._finish_init)
# IDs have to be used here because they cannot be applied until widget initialized
def _finish_init(self, dt):
self.ids.grid_1.cols = 5
# Loop to make 15 different buttons on screen
for x in range(15):
self.buttons.append(Button(text='button {}'.format(x)))
self.ids.grid_1.add_widget(self.buttons[x])
self.buttons[x].background_normal = 'YOUTUBE.png'
def SwitchScreen(self,*args):
sm.current = 'input_sc'
sm = ScreenManager()
sm.add_widget(InputScreen(name='input_sc'))
sm.add_widget(LandingScreen(name='landing'))
self.buttons[14].bind(on_release=SwitchScreen)
# Input screen
class InputScreen(Screen):
pass
class MySubApp(App):
def build(self):
return MyScreenManager()
def main():
Builder.load_string(kv_text)
app = MySubApp()
app.run()
if __name__ == '__main__':
main()
If someone could help me understand the hole in my current logic I would appreciate it greatly. Thanks.
Each screen has a manager property that gives you the instance of the ScreenManager used. You only need to use it to refer to the ScreemManager instance and use its current method:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.clock import Clock
# Kivy Formatting
kv_text='''\
<MyScreenManager>:
LandingScreen:
InputScreen:
<InputScreen#Screen>:
name: 'input_sc'
AnchorLayout:
id: anchor_1
Button:
text: 'Hello'
<LandingScreen#Screen>:
name: 'landing_sc'
GridLayout:
id: grid_1
cols: 5
height: 480
width: 800
spacing: 25, 20
padding: 25,25
'''
# Screen Manager
class MyScreenManager(ScreenManager):
pass
# Main screen with button layout
class LandingScreen(Screen):
def __init__(self, **kwargs):
super(LandingScreen, self).__init__(**kwargs)
self.buttons = [] # add references to all buttons here
Clock.schedule_once(self._finish_init)
# IDs have to be used here because they cannot be applied until widget initialized
def _finish_init(self, dt):
self.ids.grid_1.cols = 5
# Loop to make 15 different buttons on screen
for x in range(15):
self.buttons.append(Button(text='button {}'.format(x)))
self.ids.grid_1.add_widget(self.buttons[x])
self.buttons[x].background_normal = 'YOUTUBE.png'
self.buttons[14].bind(on_release=self.switch_screen)
def switch_screen(self, *args):
self.manager.current = 'input_sc'
# Input screen
class InputScreen(Screen):
pass
class MySubApp(App):
def build(self):
return MyScreenManager()
def main():
Builder.load_string(kv_text)
app = MySubApp()
app.run()
if __name__ == '__main__':
main()

Categories