I'm attempting to create a stopwatch in Kivy. I've completed a skeleton on Sublime Text 3 (as shown in the code below). When I run the code on Sublime Text, a window opens, but Python crashes in 4.1s.
Here is the code in question:
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.properties import NumericProperty
from kivy.lang import Builder
import time
class CrudeTimerGrid(GridLayout):
# Initialise timer with input start time
def __init__(self,start_time):
time = NumericProperty(start_time)
def tick(self):
if self.time > 0:
self.time -= 1
else:
pass
def start(self):
Clock.schedule_interval(self.tick,1)
def pause(self):
Clock.unschedule()
# incomplete code
def reset(self):
pass
class CrudeTimerApp(App):
def build(self):
# Testing timer by initialising timer with 20 seconds
return CrudeTimerGrid(20)
Builder.load_string('''
<CrudeTimerGrid>
id: timer
rows: 2
# insert formatting here
BoxLayout:
Label:
text: timer.time
BoxLayout:
Button:
text: "Start"
on_press: timer.start()
Button:
text: "Pause"
on_press: timer.pause()
Button:
text: "Reset"
on_press: timer.reset()
''')
CrudeTimerApp().run()
Newbie at StackOverflow too, so please let me know if any other info is needed. Thanks for your help!
Main problem is here:
def __init__(self,start_time):
time = NumericProperty(start_time)
You should define Kivy properties at class level, please read this. Code'll stop crash if you change it like this:
class CrudeTimerGrid(GridLayout):
time = NumericProperty(0)
There're also few other changes you should do to make it finally work, here's full code version:
import kivy
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock
from kivy.properties import NumericProperty
from kivy.lang import Builder
import time
class CrudeTimerGrid(GridLayout):
time = NumericProperty(0)
def tick(self, *_):
if self.time > 0:
self.time -= 1
else:
pass
def start(self, *_):
self.cb = Clock.schedule_interval(self.tick,1)
def pause(self):
Clock.unschedule(self.cb)
# incomplete code
def reset(self, *_):
pass
class CrudeTimerApp(App):
def build(self):
# Testing timer by initialising timer with 20 seconds
return CrudeTimerGrid(time=20)
Builder.load_string('''
<CrudeTimerGrid>
id: timer
rows: 2
# insert formatting here
BoxLayout:
Label:
text: str(timer.time)
BoxLayout:
Button:
text: "Start"
on_press: timer.start()
Button:
text: "Pause"
on_press: timer.pause()
Button:
text: "Reset"
on_press: timer.reset()
''')
CrudeTimerApp().run()
Upd:
Any idea why 2 arguments are input into tick in the first place?
tick passed to Clock.schedule_interval to be called. This function schedules it's callback with additional parameter (as many other functions in Kivy also do). You can read a bit more about it in documentation.
You defined self.cb in order to reference it in Clock.unschedule,
correct?
You can schedule many different functions with different intervals calling Clock.schedule_interval multiple times. But how can Clock.unschedule know which concrete of them to unschedule? In order to make Clock.unschedule know what to unschedule you should pass to it value returned by Clock.schedule_interval. Here's documentation section where this mechanism described.
Related
I've got this simple code to change screens. I'm just wondering what the correct variable to change in callback() is to invoke a screen change. I've seen this done other ways, but wanted to make sure I have a static MDToolbar so when the screen changes, the toolbar does not move.
The code below does does not change screen in callback()in this
.py code:
from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivymd.app import MDApp
from kivy.loader import Loader
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.label import MDLabel
from kivymd.uix.floatlayout import MDFloatLayout
class TCalc(MDApp):
def callback(self,obj):
self.root.current = "tipscreen2"
def build(self):
return Builder.load_file('dip.kv')
TCalc().run()
dip.kv:
MDNavigationLayout:
orientation: 'vertical'
MDToolbar:
id: toolbar
right_action_items: [["cog", lambda x: app.callback(x)]]
ScreenManager:
id: screen_manager
Screen:
name: "tipscreen1"
MDFloatLayout:
MDLabel:
text: "Screen 1"
Screen:
name: "tipscreen2"
MDFloatLayout:
MDLabel:
text: "Screen 2"
MDNavigationDrawer:
id: nav_drawer
MDNavigationDrawerMenu:
The code below does does not change screen in callback()in...
That's because in method callback you did self.root.current. This is supposed to be applicable if your self.root is a ScreenManager instance which is indeed not the case here.
Now, by doing lambda x: app.callback(x) you are actually passing the instance itself (here, MDActionTopAppBarButton). So if you want to pass some var. through the callback method to change screens, one of the different ways could be simply pass the ScreenManager object (here a weak reference actually). Or, you can just access the ScreenManager by ids (again a weak reference) directly from python. Both way the solution could be something like this,
In kvlang,
right_action_items: [["cog", lambda x: app.callback(x)]]
# or, right_action_items: [["cog", app.callback]]
In python,
def callback(self,sm):
# or, def callback(self, *args):
sm.current = "tipscreen2"
# or, self.root.ids.screen_manager.current = "tipscreen2"
I saw that there are other topics related to this one but I could make this simple project to work.
I need a label to be updated with the function status while it is running just to let the user know that the program still working while the function is processing, but the label is only updated when the function is completed
Here are the files
my.kv
WindowManager:
ChangeLabel:
<ChangeLabel>:
BoxLayout:
orientation: 'vertical'
padding: 200, 20
Label:
id: labeltxt
text: 'Text to change'
font_size: 30
height: 50
Button:
text:'Run Function'
size_hint_y: None
height: 30
on_release:
root.changeLabel()
Label:
test.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.core.window import Window
import time
from kivy.clock import Clock
class ChangeLabel(Screen):
def changeLabel(self):
Clock.schedule_once(self.test)
def test(self, dt):
self.ids.labeltxt.text = 'Function is running...'#I wanna this in lablel while function is running
time.sleep(2)# function to run
self.ids.labeltxt.text = 'Completed'
class WindowManager (ScreenManager):
pass
kv = Builder.load_file('my.kv')
class MyApp(App):
def build(self):
return kv
MyApp().run()
I did this solution, now it is working the way I want, but not sure if this is the best way.
test.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang.builder import Builder
from kivy.core.window import Window
import time
from kivy.clock import Clock
class ChangeLabel(Screen):
i = 0
def changeLabel(self):
Clock.schedule_interval(self.test, 0.05)
def test(self, dt):
if self.i == 0:
self.ids.labeltxt.text = 'Function is running...'
self.i = 1
else:
time.sleep(2)# function to run
self.ids.labeltxt.text = 'Completed'
self.i = 0
Clock.unschedule(self.test)
class WindowManager (ScreenManager):
pass
kv = Builder.load_file('my.kv')
class MyApp(App):
def build(self):
return kv
MyApp().run()
I am hoping to eventually create a button to pause/start/end a pong app I built as a learning exercise. However, to understand the basics of how to create a button in the first place, and assign a response to it I am just wanting to create a button that notifies me every time the button state changes. Here is the python code I have so far:
import kivy.uix.button as kb
from kivy.app import App
from kivy.uix.widget import Widget
class Button_Widget(Widget):
def callback(instance, value):
print('The button <%s> state is <%s>' % (instance, value))
btn1 = kb.Button(text='Hello World 1')
btn1.bind(on_press=callback)
class ButtonApp(App):
def build(self):
button = Button_Widget()
return button
with the associated kv file:
#:kivy 1.0.9
<Button_Widget>:
size: 100, 100
canvas:
Rectangle:
pos = self.pos
size = self.size
So far the only resources I have found are these (1 and 2) Kivy turorials, which aren't too helpful. Or at least, I don't understand their language enough yet for them to be useful
Let's see the simplest code to create a button in kivy
from kivy.app import App
from kivy.uix.button import Button
class your_app_name(App):
def build(self):
button = Button(text="Button")
return button
your_app_name().run()
You can also add command or event to a button by adding on_press= function_name
Solution
In the class Button_Widget(), you have to override the constructor, __init__() so that you can add the button widget to the root widget. Please refer to the example for details.
References
Programming Guide » Kivy Basics » Create an application
Programming Guide » Kv language
Kivy Language
Event dispatcher » bind() Bind an event type or a property to a callback.
Example
main.py
import kivy.uix.button as kb
from kivy.app import App
from kivy.uix.widget import Widget
class Button_Widget(Widget):
def __init__(self, **kwargs):
super(Button_Widget, self).__init__(**kwargs)
btn1 = kb.Button(text='Hello World 1')
btn1.bind(on_press=self.callback)
self.add_widget(btn1)
def callback(self, instance):
print('The button %s state is <%s>' % (instance, instance.state))
class ButtonApp(App):
def build(self):
return Button_Widget()
if __name__ == "__main__":
ButtonApp().run()
button.kv
#:kivy 1.10.1
<Button_Widget>:
size: 100, 100
canvas:
Rectangle:
pos: self.pos
size: self.size
Output
I'm trying to make a simple application in Python 3.5 and kivy that starts with a simple screen and when you click on it, goes to another one which shows 3 lists that let you chose the data:
The Python file:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.listview import ListItemButton
from kivy.properties import ListProperty
from dataTopy import rlists
# Transition des ecrans:
class MainScreen(Screen):
pass
class AnotherScreen(Screen):
pass
class ScreenManagement(ScreenManager):
pass
presentation = Builder.load_file("ex44.kv")
#
class FirstListItemButton(ListItemButton):
pass
class SecondListItemButton(ListItemButton):
pass
class ThirdListItemButton(ListItemButton):
pass
class Ex44(BoxLayout):
d1 = ListProperty([str(i) for i in range(1990,2014)] )
d2 = ListProperty(['']*100)
d3 = ListProperty(['']*100)
def change(self,c):
try: self.d2,self.d3 = rlists(int(c.text))
except:
import os
CurDir = os.getcwd()
print('Can not find data in ' + CurDir)
def change1(self,c):
print('M => '+c.text)
def change2(self,c):
print('F => '+c.text)
class Ex44App(App):
def build(self):
return presentation
if __name__ == '__main__':
Ex44App().run()
The kivy file:
#: import FadeTransition kivy.uix.screenmanager.FadeTransition
#: import ListAdapter kivy.adapters.listadapter.ListAdapter
#: import ex44 ex44
ScreenManagement:
transition: FadeTransition()
MainScreen:
AnotherScreen:
<MainScreen>:
name: "main"
Button:
on_release: app.root.current = "other"
text: "Next Screen"
font_size: 50
<AnotherScreen>:
name: "other"
BoxLayout:
Ex44
Button:
color: 0,1,0,1
font_size: 25
size_hint: 0.3,0.2
text: "Back Home"
on_release: app.root.current = "main"
pos_hint: {"right":1, "top":1}
<FirstListItemButton>:
on_press: app.root.change(*args)
<SecondListItemButton>:
on_press: app.root.change1(*args)
<ThirdListItemButton>:
on_press: app.root.change2(*args)
<Ex44>:
ListView:
adapter:
ListAdapter(data=root.d1,
selection_mode='single',
cls=ex44.FirstListItemButton)
ListView:
adapter:
ListAdapter(data=root.d2,
selection_mode='single',
cls=ex44.SecondListItemButton)
ListView:
adapter:
ListAdapter(data=root.d3,
selection_mode='single',
cls=ex44.ThirdListItemButton)
When I try to run the app, it tells me: "Unknown class "
It is weird because the class Ex44 works alone but not when I'm trying to add it into the main application logic.
I've tried to return a widget instead of a BoxLayout for the class, to return Ex44 alone in the kivy file, etc. but I always get the same error in return.
Is it possible to return a BoxLayout inside another one in Kivy?
You are building the kv file too soon (before the class is defined). move the Builder.from_file call to the build method
...
def build(self):
return Builder.load_file("ex44.kv")
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