How to use on_enter event to change screens on kivy? - python-3.x

So, here is what I'm trying to do: when entering on the first screen of my app, I want it to check if some files exist in given directory. If they exist, I want it to immediately change to another screen.
I've tried the following:
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from os import listdir
class Manager(ScreenManager):
pass
class CreateFileScreen(Screen):
def on_enter(self):
try:
files = listdir("data/files")
if "file.dat" in files:
self.parent.current = "login"
else:
pass
except FileNotFoundError:
pass
class LoginScreen(Screen):
pass
class ExampleApp(App):
def build(self):
return Manager()
if __name__ == "__main__":
ExampleApp().run()
example.kv
#:kivy 1.10.0
<CreateFileScreen>:
BoxLayout:
Label:
text: "This is Create File Screen"
font_size: "30sp"
<LoginScreen>:
BoxLayout:
Label:
text: "This is Login Screen"
font_size: "30sp"
<Manager>:
CreateFileScreen:
name: "createfile"
LoginScreen:
name: "login"
When file.dat does exist in data/files I get the following error:
kivy.uix.screenmanager.ScreenManagerException: No Screen with name "login".
Any idea on how to fix this?

The problem is that on_enter is executed before the screen gets its name.
You can make a change_screen method, then call it with Clock.schedule_once. That way it will be called the next frame.
from kivy.clock import Clock
class CreateFileScreen(Screen):
def on_enter(self):
Clock.schedule_once(self.change_screen)
def change_screen(self, dt):
try:
files = listdir("data/files")
if "file.dat" in files:
self.manager.current = "login"
else:
pass
except Exception as e:
print(e)

Related

Failed to add widgets dynamically in output screen in .kv file using python

I was trying to add the result to a new screen whose content should be dynamic. I tried a few of the trivial ways given in StackOverflow itself but failed to show any content. my code goes as such.
I am entering multiple data, separated by "," without spaces, using text input, and then splitting it which will be stored as a list. The data will be parsed and an equivalent number of labels should be shown on the output screen which I tried but was not successful in execution (line 51-56 in anbs.py).
For example, the input is "I,am,a,good,boy".
The result should be as it will be in the console, i.e. text of each Label should contain an individual item, but nothing goes to the output screen... just a big button that is used to traverse between the screens.
This is my anbs.py file.
from kivy.app import App
from kivy.lang.builder import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.factory import Factory
Window.size = (600, 600)
class HelpWindow(Screen):
"""
This is a help window. It contains the functionality of all the given boxes
on the main window.
"""
def main_window(self):
sm.current = "main"
class MainWindow(Screen):
"""
This is the main window that contains the main form.
This connects the frontend of the app to the backend
"""
target = ObjectProperty(None)
def v_popup(self):
version_popup()
def help(self):
sm.current = "help"
def output_window(self):
sm.current = "output"
def get_results(self):
#OutputWindow.main(options = options)
out_window = OutputWindow()
out_window.main(self.target.text)
sm.current = "output"
class OutputWindow(Screen):
"""
This is the output window. All the generated results will be seen here.
"""
res = ObjectProperty(None)
res_out = ObjectProperty(None)
def main(self, options):
options = list(options.split(","))
for item in options:
print(item)
self.res_out.add_widget(Label(text=item))
def main_window(self):
sm.current = "main"
class WindowManager(ScreenManager):
pass
def version_popup():
"""
Version Popup Window.
"""
version = "v1.0"
version_text = "this is "+version+" for this app"
vpop = Popup(title="Version",
content=Label(text=version_text),
size_hint=(None, None), size=(400, 400))
vpop.open()
### main builder and WindowManager object
kv = Builder.load_file("start.kv")
sm = WindowManager()
### Adding screens to widget
screens = [MainWindow(name="main"), HelpWindow(name="help"), OutputWindow(name="output")]
for screen in screens:
sm.add_widget(screen)
sm.current = "main"
### main working
class AnbsApp(App):
def build(self):
return sm
if __name__ == '__main__':
AnbsApp().run()
and my start.kv file looks like this
<HelpWindow>
name:"help"
Button:
id:ms
text:"Main Screen"
on_release:
root.manager.transition.direction = "left"
root.main_window()
<MainWindow>
name:"main"
target : target
GridLayout:
cols:1
GridLayout:
cols:3
row_force_default: True
row_default_height: 50
Button:
id:hp
text:"Help"
on_release:
root.manager.transition.direction = "right"
root.help()
Button:
id:version
text: "Version"
on_release:
root.v_popup()
GridLayout:
cols:2
row_force_default: True
row_default_height: 30
Label:
text:"Target *"
TextInput:
id:target
multiline:False
GridLayout:
cols:3
row_force_default: True
row_default_height: 50
Label:
text:""
Button:
text:"Submit"
on_release:
root.get_results()
Label:
text:""
<OutputWindow>
name:"output"
res_out:res_out
GridLayout:
id:res_out
cols : 1
Button:
id:ms
text:"Main Screen"
on_release:
root.manager.transition.direction = "right"
root.main_window()
I can't really say whats the major point I am missing in it.
Your code:
def get_results(self):
#OutputWindow.main(options = options)
out_window = OutputWindow()
out_window.main(self.target.text)
sm.current = "output"
is creating a new instance of OutputWindow in the line:
out_window = OutputWindow()
and then calling the main() method of that new instance. However, that new instance is not in your GUI, so no effect is observed. To correct that, change the code to use the instance of OutputWindow that is in your GUI:
def get_results(self):
out_window = self.manager.get_screen('output') # get OutputWindow instance
out_window.main(self.target.text)
sm.current = "output"
You will also need to adjust the size/position of the Button in the OutputWindowso that it does not completely cover the GridLayout.

AttributeError: 'Third_Window' object has no attribute 'history'

I am a beginner and i am trying to do a password saver app. In this i am getting a error like AttributeError: 'Third_Window' object has no attribute 'history'.
And i also want how to Label a text that is inside a file
In .py file
from kivy.app import App
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.properties import ObjectProperty
from kivy.core.window import Window
from kivy.lang import Builder
Window.clearcolor = 0, 0, 1, 0
class Second_Window(Screen):
pass
class Third_Window(Screen):
def btn2(self):
global history
file1 = open('users_input.txt', 'r')
Lines = file1.readlines()
for line in Lines:
print("{}".format(line.strip()))
self.history = ""
self.history += "{}".format(line.strip())
print(history)
class Screen_Manager(ScreenManager):
pass
class Main_Window(Screen):
pass
presentation = Builder.load_file("password_saver.kv")
class Password_Saver(App):
def build(self):
return presentation
if __name__ == "__main__":
Password_Saver().run()
In .kv file
Screen_Manager:
Main_Window:
Second_Window:
Third_Window:
<Main_Window>:
name: 'main'
<Second_Window>:
name: 'second'
<Third_Window>:
name: 'third'
GridLayout:
cols: 1
Label:
text: root.history
Button:
text: "Go Back"
on_release:
root.btn2()
app.root.current = 'main'
Please help me to solve this issue
Thanks!!
In kivy file you are using root.history. root in that part of your kv file is third screen. To access something frpm .py file use app. instead of root..

ObjectProperty has no attribute (Kivy, Python 3.x)

This questions has been asked and answered quite often, but I still can't get it to work. I want to access a widget that is a children of another widget. For this I used an ObjectProperty. When I try to access the ObjectProperty (by opening any file via the FileChooser popup) to change the label's text I get this error:
self.pdfpages.text = "change to this" AttributeError:
'kivy.properties.ObjectProperty' object has no attribute 'text'
Do I need to initialize the ObjectProperty? Or is there a problem with my .kv structure?
main.py
import kivy
kivy.require('1.10.0') # replace with your current kivy version !
# Kivy Imports
from kivy.app import App
#UI Eleemnts
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.popup import Popup
from kivy.uix.scrollview import ScrollView
from kivy.uix.scatter import Scatter
from kivy.uix.image import Image
#Core Elements
from kivy.core.image import Image as CoreImage
from kivy.properties import ObjectProperty
# Python Imports
import os
class Window(BoxLayout):
#add child widgets (in kv file)
pass
class PDFView(ScrollView):
pdfpages = ObjectProperty()
def newPage(self, filepath):
#here the error occurs
self.pdfpages.text = "change to this"
class SideBar(BoxLayout):
def openfile(self):
print("Button pressed")
OpenDialog().open()
class OpenDialog(Popup):
def cancelfile(self):
#close popup
self.dismiss()
print("No file opened")
def openfile(self, path, selection):
#selection can contain multiple files, [0] is first or only
self.dismiss()
print("Opened File: " + os.path.join(path, selection[0]))
#open PDFView class
PDFView.newPage(PDFView, os.path.join(path, selection[0]))
class PDFEditor(App):
title = "PDFEditor"
#gets called on startup
#load program
def build(self):
#return root node
return Window()
if __name__ == '__main__':
PDFEditor().run()
PDFEditor.kv
<Window>:
#this is the main (root) "window"
orientation: "horizontal"
SideBar:
size_hint: (.1, 1)
PDFView:
size_hint: (.9, 1)
<PDFView>:
#handler for ObjectProperty
pdfpages: pdfpages
Scatter:
#only zoom
BoxLayout:
#add Images in here somehow
orientation: "vertical"
Label:
id: pdfpages
text: "hi"
<SideBar>:
orientation: "vertical"
Button:
id: btn_openfile
on_release: root.openfile()
text: "Open File"
Label:
text: "Hello!"
<OpenDialog>:
title: "Choose File"
BoxLayout:
#Fullscreen
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: chsr_open
BoxLayout:
size_hint_y: None
height: 30
Button:
text: "Cancel"
on_release: root.cancelfile()
Button:
text: "Open"
on_release: root.openfile(chsr_open.path, chsr_open.selection)
The problem here is that you set the text of a new instance, wich is not even on the window, rather than the one you allready have.
To fix this, you need to access the one you have.
First, make your Window as an App attribute, so you can reference it later.
class PDFEditor(App):
title = "PDFEditor"
def build(self):
self.root = Window()
return self.root
Then give the PDFView an id in kv
<Window>:
orientation: "horizontal"
SideBar:
size_hint: (.1, 1)
PDFView:
id: pdfview
size_hint: (.9, 1)
Then in your openfile method, you can get the running apps root attribute you created earlier, like this.
def openfile(self, path, selection):
self.dismiss()
print("Opened File: " + os.path.join(path, selection[0]))
app = App.get_running_app()
pdf = app.root.ids.pdfview
pdf.newPage(os.path.join(path, selection[0]))
That way you can access the ids of your Window class
change your openfile method to this:
def openfile(self, path, selection):
#selection can contain multiple files, [0] is first or only
self.dismiss()
print("Opened File: " + os.path.join(path, selection[0]))
#open PDFView class
PDFView().newPage(PDFView, os.path.join(path, selection[0]))
PDFView is the class, PDFView() is an instance of this class, what you need
edit
the label does not change because you are creating a new one without replacing it. So if I understand well all you want is to replace the text of your PDFView. There is many ways to do that but I don't want to edit much your code: try this:
...
class OpenDialog(Popup):
...
def openfile(self, path, selection):
#selection can contain multiple files, [0] is first or only
self.dismiss()
print("Opened File: " + os.path.join(path, selection[0]))
#open PDFView class
app.window.ids.pdfview.newPage(PDFView, os.path.join(path, selection[0]))
...
...
class PDFEditor(App):
...
def build(self):
#return root node
self.window = Window()
return self.window
app = PDFEditor()
if __name__ == '__main__':
app.run()
in your kv:
...
<Window>:
#this is the main (root) "window"
orientation: "horizontal"
SideBar:
size_hint: (.1, 1)
PDFView:
id: pdfview
size_hint: (.9, 1)
...

How to remove the Added widget within that widget?

how can I remove the added widget from within that widget. am i making any sense? hopefully yes, hehehe...
anyway the simple code consist of buttons and label only.
My aim is if I click the add button it will show the widget which is a label & button. If i want to remove the widget i'll just click the close button in that widget.
but its not closing, its been two days already and i cant find whats the problem and its not giving me any error.
Thanks guys.
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
kv_file = Builder.load_string('''
<Screen1>:
BoxLayout:
Button:
on_release: root.add_button(True)
text: 'ADD'
size_hint: .2,.2
<Layout1>:
BoxLayout:
pos: self.x,300
size_hint: .5,.3
Label:
text: 'THIS IS A ADDED WIDGET'
Button:
text: 'Close'
on_release: root.closeBTN()
''')
class Layout1(FloatLayout):
def closeBTN(self):
AddWidget_Layout1().addEmps(True)
class AddWidget_Layout1(Widget):
def __init__(self, **kwargs):
super(AddWidget_Layout1,self).__init__(**kwargs)
self.count = 0
self.layout1 = Layout1()
def addEmps(self,xadd):
if xadd == 1:
self.add_widget(self.layout1)
elif xadd == True:
self.remove_widget(self.layout1)
class Screen1(Screen,AddWidget_Layout1):
def add_button(self,*args):
self.count += 1
print
if self.count == 1:
self.addEmps(1)
class projectApps(App):
def build(self):
return SM
SM = ScreenManager()
SM.add_widget(Screen1())
if __name__ == "__main__":
projectApps().run()
You have a few bugs in your code, first, you are testing xadd for 1 and True which is kinda the same thing:
def addEmps(self,xadd):
if xadd:
self.add_widget(self.layout1)
else: #was elif xadd == True which cannot happen...
self.remove_widget(self.layout1)
I would also change:
def closeBTN(self):
#AddWidget_Layout1().addEmps(True) #BAD! will create a new instance
self.parent.addEmps(False) # this looks better
Finally, I'm not sure why are you doing this since you are already using ScreenManager, so you can just switch to a different screen whenever you need instead of doing this weird widget removal thing ...
I hope this clears most of the issues

Updating a Label In Kivy

I am trying to update a label in python. It is a simple Guess the num game. Currently it prints the Computer responses in PC but I want it to print those to a label.
here is the main .py file
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
import random
secret=random.randint(1,100)
class Application(BoxLayout):
label1=""
def button_press(self):
global label1
if (int(self.user_guess.text)<secret):
print("Higher")
self.label1="Higher"
elif(int(self.user_guess.text)>secret):
print("Lower")
self.label1="Lower"
elif(int(self.user_guess.text)==secret):
print("You WOn")
self.label1="You WOn"
class WeatherApp(App):
pass
if __name__ == '__main__':
WeatherApp().run()
the .kv file
Application:
<Application>:
size:(480,30)
user_guess:guess
size_hint:(None,None)
text:""
orientation: "vertical"
BoxLayout:
Button:
text:'Play'
on_press:root.button_press()
TextInput:
id:guess
text:''
Label:
text:root.label1
I think you should use
self.label1.text="Higher"

Resources