Outputting the input text in a new screen - python

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

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

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)

multiple widgets in Kivy

I am new to python front-end. Currently i am stuck with integrating 2 python kivy programs.
Each of them have different widget. One is asynchronous image loading and one is clock widget.
Can anyone help me to integrate this multiple widgets in a sigle python file. I am adding my python codes below. please help me.
Asynchronus image loading
from kivy.app import App
from kivy.uix.image import AsyncImage
from kivy.lang import Builder
Builder.load_string('''
<CenteredAsyncImage>:
allow_stretch: True
keep_ratio: True
size_hint_y: None
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
height: dp(800)
mipmap: True
''')
class CenteredAsyncImage(AsyncImage):
pass
class TestAsyncApp(App):
def build(self):
img = 'edited_background.jpg'
return CenteredAsyncImage(source=img)
if __name__ == '__main__':
TestAsyncApp().run()
Clock Widget
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.base import Builder
from kivy.uix.image import Image
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.clock import Clock
from kivy.core.window import Window
import time
class Time(Label):
def updateTime(self,*args):
self.text = time.asctime()
class TimeApp(App):
def build(self):
t=Time()
Clock.schedule_interval(t.updateTime,1)
return(t)
TimeApp().run()
I need to display both in a single window and clock should be in the top right most corner.
Here is a way to do that:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from centeredasyncimage import CenteredAsyncImage
from timelabel import Time
theroot = Builder.load_string('''
RelativeLayout:
CenteredAsyncImage:
source: 'img.jpg'
size_hint: 0.25, 0.25
pos_hint: {'x': 0, 'top': 1.0}
Time:
id: time
size_hint: 0.25, 0.25
pos_hint: {'right': 1.0, 'top': 1.0}
''')
class TheApp(App):
def build(self):
# schedule the start of the clock
Clock.schedule_once(self.start_time_updates)
return theroot
def start_time_updates(self, dt):
t = self.root.ids.time
# schedule the clock update every second
Clock.schedule_interval(t.updateTime,1)
if __name__ == '__main__':
TheApp().run()
This assumes that the CenteredAsyncImge is defined in a file named centeredasyncimage.py and Time is defined in a file named timelabel.py.
In those files, the code that is used to run an App needs to be protected from running when the file is imported. That is done using if __name__ == '__main__':. So, I have modified those files slightly:
timelabel.py:
from kivy.uix.label import Label
import time
class Time(Label):
def updateTime(self,*args):
self.text = time.asctime()
if __name__ == '__main__':
from kivy.app import App
from kivy.clock import Clock
class TimeApp(App):
def build(self):
t=Time()
Clock.schedule_interval(t.updateTime,1)
return(t)
TimeApp().run()
and centeredasyncimage.py:
from kivy.uix.image import AsyncImage
from kivy.lang import Builder
Builder.load_string('''
<CenteredAsyncImage>:
allow_stretch: True
keep_ratio: True
size_hint_y: None
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
height: dp(800)
mipmap: True
''')
class CenteredAsyncImage(AsyncImage):
pass
if __name__ == '__main__':
from kivy.app import App
class TestAsyncApp(App):
def build(self):
img = 'edited_background.jpg'
return CenteredAsyncImage(source=img)
TestAsyncApp().run()
If you want to update the image source for the CenteredAsyncImage, you can just modify the file containing the App as:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import StringProperty
from centeredasyncimage import CenteredAsyncImage
from timelabel import Time
theroot = Builder.load_string('''
RelativeLayout:
CenteredAsyncImage:
id: casi
source: 'img.jpg' # default image (this is optional)
size_hint: 0.25, 0.25
pos_hint: {'x': 0, 'top': 1.0}
Time:
id: time
size_hint: 0.25, 0.25
pos_hint: {'right': 1.0, 'top': 1.0}
''')
class TheApp(App):
img_source = StringProperty() # property to hold image source
def build(self):
self.bind(img_source=self.set_casi_source) # bind source property
Clock.schedule_once(self.start_time_updates)
return theroot
def set_casi_source(self, app, val, *args):
# this sets the image source
self.root.ids.casi.source = val
def start_time_updates(self, dt):
t = self.root.ids.time
Clock.schedule_interval(t.updateTime,1)
if __name__ == '__main__':
TheApp().run()
The img_source StringProperty will contain the source for the CenteredAsyncImage. The call to self.bind arranges for any change to the img_source to trigger a call to set_casi_source(), which sets the source for the CenteredAsyncImage. Note that setting the img_source inside the build() method will fail, since it uses ids and they are not setup yet in the build() method. So, any time after the App is built, just changing the img_source property will change the CenteredAsyncImage.

Kivy : My label text is not updating after adding screens

So I wanted to read data from my Arduino through serial port, and update the data I read to a label text to display. It is working when I only have simple code to just read and update, but when I add in the ScreenManager and Screen, it stops updating the text.
Eventually I will need to have different animation according to the data I received, this is more on a testing if this function works
Thanks in Advance!
Here's my entire code
import os
os.environ['KIVY_GL_BACKEND'] ='gl'
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty, ListProperty)
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.text import LabelBase
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
LabelBase.register(name='Sans',fn_regular="Sansation-Regular.ttf")
import serial
kivy = Builder.load_string("""
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<MyManager>:
transition: FadeTransition()
MainScreen:
OperationScreen:
<MainScreen>:
name: 'main'
Label:
text: 'Welcome'
font_size: 40
on_touch_up : app.root.current = 'operation'
Label:
text: 'dafault'
font_size: 20
pos: -200,-100
id: data_label
<OperationScreen>:
name: 'operation'
Label:
text: 'Youre in'
font_size: 40
""")
class OperationScreen(Screen):
pass
class MainScreen(Screen):
def __init__(self,**kwargs):
super(MainScreen,self).__init__(**kwargs)
def Read(self,dt):
Clock.unschedule(self.Read)
data = arduino.readline()
if data != '':
self.ids.data_label.text = data
Clock.schedule_once(self.Read)
pass
class MyManager(ScreenManager):
pass
class mainApp(App):
Main = MainScreen()
def build(self):
Clock.schedule_once(self.Main.Read)
return MyManager()
if __name__ == '__main__':
try:
arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
except:
print("failed to connect")
mainApp().run()
I expect the label with text 'default' to be changing accordingly but it just froze with the 'default'
Problem
When your app runs, there are two instances of class MainScreen. One was instantiated in kv file. The other one was instantiated manually, Main = MainScreen() in class mainApp.
The scheduling of method Read() is in the instance created manually, Main = MainScreen() and there is no modal view associated with this one.
Solution
In kv file, add id: main_screen for MainScreen:
Remove Main = MainScreen() in class mainApp
Implement a constructor for class MyManager()
Move the scheduling from class mainApp into the constructor of class MyManager()
In your case, it is better to use Clock.create_trigger() instead of Clock.schedule_once()
The correct way to cancel a Clock event is either event.cancel() or Clock.unschedule(event)
Example
main.py
import os
os.environ['KIVY_GL_BACKEND'] = 'gl'
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, StringProperty, ReferenceListProperty, ObjectProperty, ListProperty)
from kivy.clock import Clock
from kivy.vector import Vector
from kivy.core.text import LabelBase
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
# LabelBase.register(name='Sans', fn_regular="Sansation-Regular.ttf")
import serial
kivy = Builder.load_string("""
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
<MyManager>:
transition: FadeTransition()
MainScreen:
id: main_screen
OperationScreen:
<MainScreen>:
name: 'main'
Label:
text: 'Welcome'
font_size: 40
on_touch_up : app.root.current = 'operation'
Label:
text: 'dafault'
font_size: 20
pos: -200,-100
id: data_label
<OperationScreen>:
name: 'operation'
Label:
text: 'Youre in'
font_size: 40
""")
class OperationScreen(Screen):
pass
class MainScreen(Screen):
def Read(self, dt):
data = str(dt)
# data = arduino.readline()
if data != '':
self.ids.data_label.text = data
self.manager.event_trigger()
else:
self.manager.event_trigger.cancel()
class MyManager(ScreenManager):
event_trigger = ObjectProperty(None)
def __init__(self, **kwargs):
super(MyManager, self).__init__(**kwargs)
self.event_trigger = Clock.create_trigger(self.ids.main_screen.Read)
self.event_trigger()
class mainApp(App):
def build(self):
return MyManager()
if __name__ == '__main__':
# try:
# arduino = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
# except:
# print("failed to connect")
mainApp().run()
Output

Get variable declared in another Screen

I am working on one simple Kivy GUI application. What i am trying to do is that i am want to access one object 'obj_random' of class 'CL_Random' from second screen(in which it is not created). I just don't know how to access declared in one screen to different screen.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
#from kivy.uix.boxlayout import BoxLayout
#from kivy.uix.popup import Popup
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
class CL_FirstWindow(Screen):
def change_screen(self):
obj_random = CL_Random()
self.parent.current = 'second_window'
class CL_SecondWindow(Screen):
scond_prop = ObjectProperty(None)
def change_screens(self):
self.parent.current = 'first_window'
class ScreenManagement(ScreenManager):
pass
class CL_Random:
def __init__(self):
self.x = 5
presentation = Builder.load_file("rough2.kv")
class app(App):
def build(self):
return presentation
obj_app = app()
obj_app.run()
rough2.kv
ScreenManagement:
CL_FirstWindow:
CL_SecondWindow:
<CL_FirstWindow>:
id: first_window_id
name: 'first_window'
FloatLayout:
Button:
text: 'Button1'
size_hint: 0.3, 0.12
pos_hint: {'x':0.1,'y':0.6}
on_release: root.change_screen()
<CL_SecondWindow>:
id: second_window_id
name: 'second_window'
FloatLayout:
Button:
text: 'Button2'
size_hint: 0.3, 0.12
pos_hint: {'x':0.1,'y':0.6}
on_release: root.change_screens()
Label:
id: second_label_id
text: 'SECOND SCREEN'
pos_hint: {'x': 0.20, 'y':0.8}
size_hint: 1, 0.1
font_size: 35
halign: 'left'
valign: 'middle'
text_size: self.size
Thnks
To access a variable that is declared within a class the first thing is that this variable must be an attribute of the class so that the scope is greater, but as in your case the variable is local and it will be eliminated when it finishes executing the function.
On the other the solution is based on accessing the other Screen via the ScreenManager, and having the object access to the attribute for it we use the get_screen() method:
CL_SecondWindow --> ScreenManager --> CL_FirstWindow --> obj_random
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
class CL_FirstWindow(Screen):
def change_screen(self):
self.obj_random = CL_Random()
self.manager.current = 'second_window'
class CL_SecondWindow(Screen):
scond_prop = ObjectProperty(None)
def change_screens(self):
first_screen = self.manager.get_screen('first_window')
print(first_screen.obj_random.x)
self.parent.current = 'first_window'
class ScreenManagement(ScreenManager):
pass
class CL_Random:
def __init__(self):
self.x = 5
presentation = Builder.load_file("rough2.kv")
class app(App):
def build(self):
return presentation
obj_app = app()
obj_app.run()

Categories

Resources