Running my code seems to end in an Exception error, I have tried for a long time to debug this but I couldn't do it
main.py
from kivy.animation import Animation
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition
from gpshelper import GpsHelper
import plyer
class Manager(ScreenManager):
pass
class SelectionScreen(Screen):
def on_start(self):
GpsHelper().run()
class Game(Screen):
pass
class GpsBlinker():
def blink(self):
anim = Animation(opacity=0, blink_size=50)
anim.bind(on_complete=self.reset)
anim.start(self)
def reset(self, *args):
self.opacity = 1
self.blink_size = self.default_blink_size
self.blink()
kv = Builder.load_file("hiby.kv")
class HeIsBehindYou(App):
def build(self):
return kv
if __name__ == '__main__':
HeIsBehindYou().run()
main.kv
#:import FadeTransition kivy.uix.screenmanager.FadeTransition
Manager:
transition: FadeTransition()
SelectionScreen:
name:'selectionscreen'
Game:
name:'game'
<GpsBlinker>
default_blink_size: 25
blink_size: 25
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
RoundedRectangle:
radius:[root.blink_size/2.0, ]
size: [root.blink_size, root.blink_size]
pos: root.pos[0] + root.size[0]/2.0 - root.blink_size/2.0, root.pos[1] + root.size[1]/2.0 - root.blink_size/2.0
<SelectionScreen>
BoxLayout:
Image:
source: 'placeholder.png'
size: self.texture_size
Button:
text: 'Start Day 1'
on_release:
app.root.current = "game"
root.manager.transition.direction = "up"
print("inside")
root.on_start()
<Game>
id: game
BoxLayout:
orientation: 'vertical'
Image:
source: 'digital-grid-background.jpg'
size: self.texture_size
size_hint: 1, 3
GpsBlinker:
id: blinker
After I run this I get the following error:
raise FactoryException('Unknown class <%s>' % name)
kivy.factory.FactoryException: Unknown class <GpsBlinker
I tried to make a gps module to that overlays an animation in the game(screen). Even tho I have it defined in the main.py file, I still get this error
Related
I am having trouble understanding how to inherit the functionality of one class into another with Kivy. I understand the error message ('LoadDialog_Directory' object has no attribute 'manager'), but I'm just not grasping exactly how to fix it. I believe I need to do something with the function below, but ultimately, I do not know.
def __init__(self, **kwargs):
super().__init__(**kwargs)
The goal of this script is to be able to select a specific driver as the path for the filechooser. I choose this method vs. others because most were using Kivy 1.11.1, this version has a lot of deprecated functions that do not work with 2.0.
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
def dir_driver(self):
x = self.manager.get_screen("first")
return str(x.ids.drive_dir.text)
class FirstWindow(Screen):
def get_drives(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.dir_driver()
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)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
text: "Root Drive"
halign: 'center'
option_cls: "MyOption"
values: root.get_drives()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Side Note: The reason for the extra blank TextInput is because the Spinner will not function (show drivers) if it is taking too much of the App.
After a few hours of trail and error I finally got it to work, but I have no idea why it works. Here is what changed:
New variable in class LoadDialog_Directory(FloatLayout) - input_pth
Called input_pth into my content variable within class FirstWindow(Screen) function show_load_directory(self)
Set my filechooser.path to root.input_pth in the main.kv file.
I do not understand how the input_pth variable within content is able to reference the class LoadDialog_Directory(FloatLayout) without having to pass something like:
def __init__(self, input_pth, **kwargs):
super(LoadDialog_Directory, self).__init__()
self.input_pth = input_pth
.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.uix.spinner import SpinnerOption
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.properties import ObjectProperty
from kivy.utils import platform
from kivy.properties import StringProperty
import os
import string
class WindowManager(ScreenManager):
pass
class MyOption(SpinnerOption):
pass
class LoadDialog_Directory(FloatLayout):
input_pth = StringProperty()
load = ObjectProperty()
cancel = ObjectProperty()
class FirstWindow(Screen):
def drive_list(self):
drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
return drives
def dismiss_popup(self):
self._popup.dismiss()
def show_load_directory(self):
content = LoadDialog_Directory(load=self.directroy_path, cancel=self.dismiss_popup, input_pth=self.ids.drive_dir.text)
self._popup = Popup(title="Load file", content=content, size_hint=(0.9, 0.9))
self._popup.open()
def directroy_path(self, path, filename):
self.ids.text_input_directory.text = str(filename[0])
self.dismiss_popup()
kv_main = Builder.load_file('main.kv')
#
class MyApp(App):
def build(self):
return kv_main
if __name__ == '__main__':
MyApp().run()
main.kv
#:include first.kv
WindowManager:
FirstWindow:
<LoadDialog_Directory>:
BoxLayout:
size: root.size
pos: root.pos
orientation: "vertical"
FileChooserListView:
id: filechooser
dirselect: True
path: root.input_pth
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)
first.kv
<FirstWindow>
name: 'first'
GridLayout:
cols: 1
BoxLayout:
orientation: "horizontal"
TextInput:
BoxLayout:
orientation: "horizontal"
Spinner:
id: drive_dir
halign: 'center'
option_cls: "MyOption"
values: root.drive_list()
Button:
text: "Set Result Directory"
on_release: root.show_load_directory()
TextInput:
id: text_input_directory
disabled: True
text: text_input_directory.text
BoxLayout:
size_hint: (0.01, 1)
orientation: "horizontal"
TextInput:
Is there a way to reference a custom widget from the py file?
I made a widget in kv, but I want to reference it from py, then add it again to another widget in kv.
I tried doing this using id but I got an error (KeyError: 'words_entry').
This is what I tried:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.textinput import TextInput
import os
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
text_inputs = [self.ids.words_entry for i in range(int(self.count_input.text))]
for text_input in text_inputs:
self.manager.ids.get_input.ids.grid.add_widget(text_input)
# Switch to next screen
self.manager.current = "get_input"
class GetInput(Screen):
pass
kv_file = Builder.load_string("""
ScreenManager:
GetCount:
name: "get_count"
id: get_count
GetInput:
name: "get_input"
id: get_input
<WordEntry#TextInput>:
id: words_entry
multiline: False
size_hint: (self.width, None)
<GetCount>:
count_input: count_input
FloatLayout:
Label:
text: "count"
size_hint: 1, 0.05
pos_hint: {"top":0.9}
TextInput:
id: count_input
size_hint: 0.8, 0.05
pos_hint: {"top":0.7, "x":0.1}
multiline: False
Button:
text: "Next"
on_release: root.next()
size_hint: 0.8, 0.05
pos_hint: {"top":0.5, "x":0.1}
<GetInput>:
ScrollView:
GridLayout:
size_hint_y: None
height: self.minimum_height
id: grid
cols: 1
""")
class MainApp(App):
def build(self):
return kv_file
if __name__ == "__main__":
app = MainApp()
app.run()
In this code, I want to add WordEntry to the GridLayout in GetInput from py (the reason is that I need to add multiple depending on the user's input).
You can use Factory to create an instance of a class that has been defined in kv. So your GetCount class can be:
from kivy.factory import Factory
class GetCount(Screen):
count_input = ObjectProperty(None)
def next(self):
# Setup next screen
for _ in range(int(self.count_input.text)):
new_word_entry = Factory.WordEntry()
self.manager.ids.get_input.ids.grid.add_widget(new_word_entry)
# Switch to next screen
self.manager.current = "get_input"
I am trying to make a screen view with buttons. The problem is that the amount of buttons needed each time will change, therefore I am not able to use the kv file to make theses buttons. I am having trouble adding buttons through the normal python code. Any help is appreciated.
import kivy
import webbrowser
import MSQLHandler
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Label
from kivy.uix.widget import Widget
from kivy.uix.listview import ListItemButton
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, SlideTransition, CardTransition, SwapTransition, FadeTransition, WipeTransition, FallOutTransition, RiseInTransition, NoTransition
from kivy.lang import Builder
from kivy.uix.button import Button
class Login_Failed(Screen):
def changeScreen(self, next_screen):
self.manager.current = next_screen
class Loged_In(Screen):
def changeScreen(self, next_screen):
self.manager.current = next_screen
class Registers(Screen):
def changeScreen(self, next_screen):
self.manager.current = next_screen
class Login_Screen(Screen):
teacher_connect_image = ObjectProperty()
username_text_input = ObjectProperty()
password_text_input = ObjectProperty()
teacherid_text_input = ObjectProperty()
def LoginButton(self):
if self.teacherid_text_input.text == "":
Number = 0
else:
Number = int(self.teacherid_text_input.text)
MSQLHandler.LoginSystem(self.username_text_input.text, self.password_text_input.text, Number)
def changeScreen(self, next_screen):
self.manager.current = next_screen
if MSQLHandler.LoginSystem(self.username_text_input.text, self.password_text_input.text, Number) is True:
print("Returns True")
changeScreen(self, "Loged_In")
elif MSQLHandler.LoginSystem(self.username_text_input.text, self.password_text_input.text, Number) is False:
print("Returns False")
changeScreen(self, "Login_Failed")
else:
print("Null")
class ScreenManagerr(ScreenManager):
pass
class MainKivyFrameApp(App):
def build(self):
Registers().add_widget(Button(text="Helpp"))
return ScreenManagerr()
mainFrame = MainKivyFrameApp()
mainFrame.run()
If you look to where the app is being built, you will see: Registers().add_widget(Button(text="Helpp"))
This is where I have tried to add a Button to the screen of Registers. This doesn't give me any errors, but it still does not show the button.
Solution
In the kv file, add an event (on_enter or on_pre_enter) in each screen and bind it to a callback method as shown in the following snippets and example. Remember to remove the widgets that were added dynamically before leaving the current screen, by adding an event (on_leave or on_pre_leave).
Snippets
<Registers>:
on_pre_enter: self.add_buttons(n)
on_leave: self.remove_buttons()
Example
main.py
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.properties import ObjectProperty, NumericProperty
class MyScreenManager(ScreenManager):
total_button = NumericProperty(2)
class Login(Screen):
login = ObjectProperty(None)
def on_pre_enter(self):
Window.size = (400, 300)
def check_password(self, instance, password):
if password == "pwd":
instance.current = "registers"
class Registers(Screen):
container = ObjectProperty(None)
def on_pre_enter(self):
Window.size = (800, 600)
def add_buttons(self, n):
print("Registers: n={}".format(n))
for i in range(n):
self.container.add_widget(Button(text="Button #{}".format(i), id=str(i)))
def remove_buttons(self, *args):
for child in [child for child in self.container.children]:
self.container.remove_widget(child)
class Welcome(Screen):
pass
class TestApp(App):
title = "ScreenManager - Add Widgets Dynamically"
def build(self):
return MyScreenManager()
if __name__ == "__main__":
TestApp().run()
test.kv
#:kivy 1.10.0
#:import SwapTransition kivy.uix.screenmanager.SwapTransition
<MyScreenManager>:
transition: SwapTransition()
Login:
Registers:
on_pre_enter:
self.add_buttons(app.root.total_button)
on_leave:
self.remove_buttons()
Welcome:
<Login>:
id: login
name: "login"
login: login
GridLayout:
size_hint: (0.5, 0.5)
pos_hint: {"center_x": 0.5, "center_y": 0.6}
rows: 3
padding: 20
Label:
size_hint: (0.2, 0.2)
text:"Password:"
font_size: 30
halign: "center"
valign: "middle"
TextInput:
id: password
size_hint: (0.2, 0.06)
cursor_blink: True
font_size: 20
multiline: False
password: True
Button:
text: "Continue"
size_hint: (0.2, 0.08)
on_release:
root.login.check_password(root.manager, password.text)
<Registers>:
name:'registers'
container: container
BoxLayout:
orientation: 'vertical'
Button:
text: 'Return to Login'
on_press: root.manager.current = 'login'
Button:
text: 'Next Screen'
on_press: root.manager.current = 'welcome'
BoxLayout:
id: container
orientation: 'vertical'
<Welcome>:
name:'welcome'
BoxLayout:
Label:
text: 'Welcome!'
Button:
text: 'Return to Registers'
on_press:
app.root.total_button = 5
root.manager.current = 'registers'
Output
I'm bit confused why this is not working. Simple version of what I have is bellow. What I need is to screen manager start the main screen, on load it will start thread and do it thing updating the main screen labels... but also it should start clock. Lets say after 10 sec switch to second screen.. Until here it works... but the same needs to happen on the second screen, start clock and after 10 sec go back to main screen, this will happen in background but visually the screen will not change. Not sure what is going on :(
osd.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock, mainthread
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
import MySQLdb
import threading
Config.set('graphics', 'width', '800')
Config.set('graphics', 'height', '480')
class PauseScreen(Screen):
def on_enter(self):
print('enter')
Clock.schedule_once(self.switch_back, 5)
def switch_back(self, *args):
print('back')
self.clear_widgets()
self.parent.current = "OSD_screen"
class MainScreen(Screen):
def on_enter(self):
print('start threading')
#Clock.schedule_once(self.update_thread, 0.1)
Clock.schedule_once(self.switch, 5)
# def __init__(self, **kwargs):
# super(MainScreen, self).__init__(**kwargs)
#Clock.schedule_interval(self.update_thread, 20)
# Clock.schedule_once(self.update_thread, 0.1)
# Clock.schedule_once(self.switch, 5)
def switch(self, *args):
self.parent.current = "pause"
class ScreenManagement(ScreenManager):
stop = threading.Event()
pass
presentation = Builder.load_file("osd.kv")
class osdApp(App):
def on_stop(self):
self.root.stop.set()
def build(self):
return presentation
if __name__ == '__main__':
osdApp().run()
osd.kv
ScreenManagement:
id: screen_manager
MainScreen:
id: main_screen
name: 'OSD_screen'
manager: 'screen_manager'
PauseScreen:
id: pause_screen
name: 'pause'
manager: 'screen_manager'
<MainScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text_size: self.size
text: 'MainScreen'
font_size: 50
bold: True
halign: 'center'
valign: 'top'
size_hint: 1, .3
<PauseScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text: 'Pause Screen'
font_size: 25
The file osd.kv is loaded multiples times, you might have unwanted behaviors (For example, you will have several Screen instances with the same name). You can declare ScreenManagement as a rule in your kv file and not use Builder.load_file method:
osd.py:
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.config import Config
from kivy.uix.screenmanager import ScreenManager, Screen
import threading
Config.set('graphics', 'width', '800')
Config.set('graphics', 'height', '480')
class PauseScreen(Screen):
def on_enter(self):
print('enter')
Clock.schedule_once(self.switch_back, 5)
def switch_back(self, *args):
print('back')
self.manager.current = "osd_screen"
class MainScreen(Screen):
def on_enter(self):
print('start threading')
Clock.schedule_once(self.switch, 5)
def switch(self, *args):
self.manager.current = "pause"
class ScreenManagement(ScreenManager):
stop = threading.Event()
class OsdApp(App):
def on_stop(self):
self.root.stop.set()
def build(self):
return ScreenManagement()
if __name__ == '__main__':
OsdApp().run()
osd.kv:
<ScreenManagement>:
id: screen_manager
MainScreen:
id: main_screen
name: 'osd_screen'
PauseScreen:
id: pause_screen
name: 'pause'
<MainScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text_size: self.size
text: 'MainScreen'
font_size: 50
bold: True
halign: 'center'
valign: 'top'
size_hint: 1, .3
<PauseScreen>:
BoxLayout:
orientation: 'vertical'
Label:
text: 'Pause Screen'
font_size: 25
I have below files i am trying to switch to new windows once the button on homescreen is pressed but it throws error 'No Screen with name "VerifyScreen"'
I debugged a bit and copy pasted the Screen: part from verifyscreen.kv to our homescreen.kv file and it started working.
But why its not able to get the screen even i get the two kv files in starting only??
homescreen.kv
<HomeScreen>:
orientation: 'vertical'
screen_manager: screen_manager
ScreenManager:
size_hint: 1, 1
id: screen_manager
Screen:
BoxLayout:
orientation: 'vertical'
name: 'Home_Screen'
id: 'home'
Label:
markup: True
text: 'Home-Panel'
Button:
text: 'submitt'
size_hint: 1,1
id: submitt
pos_hint: {'center_x': .5}
on_press:
root.verify()
verifyscreen.kv
<VerifyScreen>:
orientation: 'vertical'
verify_screen_manager: verify_screen_manager
ScreenManager:
size_hint: 1, 1
id: verify_screen_manager
Screen:
name: 'VerifyScreen'
id: verify_screen
main.py
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from functools import partial
import os
import random
import kivy
kivy.require('1.8.0')
#all required modules are imported
from kivy.uix.screenmanager import Screen
from kivy.uix.screenmanager import ScreenManager
slides = [ "HomeScreen","VerifyScreen" ]
for slide in slides:
kv_file = "{}.kv".format(slide.lower())
Builder.load_file(os.path.join("slides", kv_file))
class HomeScreen(Screen):
screen_manager = ObjectProperty(None)
verify_screen_manager = ObjectProperty(None)
verify_screen = ObjectProperty(None)
def __init__(self, **kwargs):
super(HomeScreen, self).__init__(**kwargs)
self.orientation = 'vertical'
def verify(self):
print "manish"
#self.screen_manager.current = 'VerifyScreen'
self.screen_manager.switch_to(self.verify_screen)
class HomeScreenApp(App):
def build(self):
return HomeScreen()
if __name__ == '__main__':
HomeScreenApp().run()