How to make a timer + stopwatch app in kivy - python

I am trying to develop my first app with kivy but I am experiencing difficulties during development because I am still a beginner and I have no idea how to do it. The goal I have in mind is to create timer plus stopwatch app.
The timer must be very simple with a single button. Pressing the button the first time the timer starts, pressing it the second time it stops.
Later I would like to pass the time recorded with the timer to a stopwatch that puts it in a loop continuously.
For example:
the timer is activated
after 20 seconds I stop it
the stopwatch begins to decrease the value 20 to 0 every second continuously and repeat in a loop.
I tried to have a look around but I can't find anything to lean on. A valid project for the timer is the following
import kivy
from kivy.app import App
kivy.require('1.9.0')
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
Builder.load_string('''
<MainWidget>:
# Assigning the alignment to buttons
BoxLayout:
orientation: 'vertical'
# Create Button
Button:
text: 'start'
on_press: root.start()
Button:
text: 'stop'
on_press: root.stop()
Button:
text: 'Reset'
on_press: root.number = 0
# Create the Label
Label:
text: str(round(root.number))
text_size: self.size
halign: 'center'
valign: 'middle'
''')
class MainWidget(BoxLayout):
number = NumericProperty()
def __init__(self, **kwargs):
super(MainWidget, self).__init__(**kwargs)
Clock.schedule_interval(self.increment_time, .1)
self.increment_time(0)
def increment_time(self, interval):
self.number += .1
def start(self):
Clock.unschedule(self.increment_time)
Clock.schedule_interval(self.increment_time, .1)
def stop(self):
Clock.unschedule(self.increment_time)
class TimeApp(App):
def build(self):
return MainWidget()
TimeApp().run()
But in any case it has three buttons while I would only like one that acts as both a "start" and a "stop". And I have no idea how to pass the time recorde with timer to the stopwatch.
Please, if there is someone who can help me I would be truly grateful. I don't know who else to turn to and I'm going crazy about this.
Thanks in advance!
P.S.: sorry for my horrible english, i don't speak english and i tried to be as clear as possible

Related

How to clear an MDTextField without the hint_text in the wrong position

I want to clear an MDTextField. The problem is when I set the text to "", the hint_text stays in the wrong position. (It happens when I try to clear while unfocused from the MDTextField)
I tried to focus and then unfocus (from the code) on the text field to fix this, but it's not a good fix because on android the keyboard would come up. I also found this on the KivyMD documentation:
When changing a TextInput property that requires re-drawing, e.g. modifying the text, the updates occur on the next clock cycle and not instantly. This might cause any changes to the TextInput that occur between the modification and the next cycle to be ignored, or to use previous values. For example, after a update to the text, changing the cursor in the same clock frame will move it using the previous text and will likely end up in an incorrect position. The solution is to schedule any updates to occur on the next clock cycle using schedule_once().
Here's the code:
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
kv = Builder.load_string("""
<MainScreen>:
orientation: "vertical"
FloatLayout:
MDTextField:
id: textfield
hint_text: "Text Fields"
pos_hint: {"center_y":.5}
Button:
text: "Clear"
on_press: root.clear_text_field()
""")
class MainScreen(BoxLayout):
def clear_text_field(self):
self.ids.textfield.text = ""
class MainApp(MDApp):
def build(self):
return MainScreen()
if __name__ == "__main__":
app = MainApp()
app.run()

KIVY: How to multitask

I want to display a loading animation in kivy while something else is being done. How can I do that? I'm sorry I have no sample code, I just have no idea where to start.
I had the same problem, and you can use a thread to make this happen.
I'm not sure what you are trying to accomplish, but let's say you want to load something when you click a button. While loading you want to display a popup saying "loading". Here is a simple example program that would let you do this.
main.py
import threading
import time
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.label import Label
class ExampleApp(App):
def show_popup(self):
# Create and open a popup
self.loading_pop = Popup(title='Please wait',
content=Label(text='Loading...'),
size_hint=(.8, .5), auto_dismiss=False)
self.loading_pop.open()
def process_btn_click(self):
self.show_popup() # Open the popup
# Start a thread, this allows you to display the popup while
# running some long task
my_thread = threading.Thread(target=self.some_long_task)
my_thread.start()
def some_long_task(self):
current_time = time.time()
while current_time + 3 > time.time(): # 3 seconds
time.sleep(1)
# When the task is done, let the popup display "Done!"
self.loading_pop.content.text = 'Done!'
# Also let the user click out of the popup now
self.loading_pop.auto_dismiss = True
if __name__ == '__main__':
ExampleApp().run()
example.kv
Screen:
Button:
text: 'Click me'
pos_hint: {'center_x': .5, 'center_y': .5}
size_hint: .3, .2
on_release:
app.process_btn_click()
Hope this answered your question!

Python & Kivy: Camera and Coming Texts in Different Screens

I want to build an app with Kivy in Python but I got some errors that I tried to solve many times but I can't.
I'm opening a camera screen firstly. In screen, we see our webcam screen and there is 2 buttons at the bottom (Play and Capture). While I pressing Play, webcam is on and if I press Capture button, I'm taking snapshot. After taking snapshot, we are going to the screen which is located on the left. Special thanks to Erik , he built this working code below.
But when I capture the photo and we are in the left screen, I want to show some text (coming from ANOTHER py file for example basic hello1, hello2... whatever) in left screen.
I know that how can I print basically a sentence in just Console with only print function but I want to write many print function's results in my application on the left screen synchronously from ANOTHER py file.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
import time
class CheckScreen(Screen):
pass
class CameraClickScreen(Screen):
def capture(self):
camera = self.ids['camera']
timestr = time.strftime("%Y%m%d_%H%M%S")
camera.export_to_png("IMG_{}.png".format(timestr))
print("Captured")
GUI = Builder.load_string("""
GridLayout:
cols: 1
ScreenManager:
id: screen_manager
CameraClickScreen:
name: "camera_click_screen"
id: camera_click_screen
CheckScreen:
name: "check_screen"
id: check_screen
<CameraClickScreen>:
orientation: 'vertical'
GridLayout:
cols: 1
Camera:
id: camera
resolution: (640, 480)
play: False
ToggleButton:
text: 'Play'
on_press: camera.play = not camera.play
size_hint_y: None
height: '48dp'
Button:
text: 'Capture'
size_hint_y: None
height: '48dp'
on_press:
root.capture()
# root refers to <CameraClickScreen>
# app refers to TestCamera, app.root refers to the GridLayout: at the top
app.root.ids['screen_manager'].transition.direction = 'left'
app.root.ids['screen_manager'].current = 'check_screen'
<CheckScreen>:
Button:
text: "Next Screen"
font_size: 50
""")
class TestCamera(App):
def build(self):
return GUI
TestCamera().run()
Normally with this code, on the left screen, we see "Next Screen" sentence with a button. I want to see variable texts coming from py file. For example Hello 1, and clear the screen, Hello 2, and clear the screen, ..., Hello 5 and stop.
How can I add this basic print functions and integrate with app to show us on the left screen?

Kivy popup running in separate thread

I am populating a treeview in Kivy that takes some time depending on how large it is.
In the case the tree is large and takes awhile, I would like to display a popup while it is populating so the user is aware the program has not frozen, and close this popup when the logic for populating the tree finishes.
Here is what I have come up with through some research on the topic, but the popup still seems to only come once the tree is finished populating:
def show(self, *args):
self.error_popup.open()
def populate_tree(self, model):
#Clock.schedule_once(self.error_popup.open())
popup_thread = threading.Thread(target=self.show())
popup_thread.start()
# order the dictionary for better user experience
ordered_data = collections.OrderedDict(sorted(model.items()))
# logic to populate tree
for county, value in ordered_data.items():
if county != "model_name":
# set initial county dropdowns in tree
county_label = self.treeview.add_node(TreeViewButton(text=str(county), on_press=self.edit_node))
i = 0 # keep count of rules
# add rules children to county
for rule_obj, rule_list in value.items():
for rule in rule_list:
i += 1
# set rule number in tree
rule_label = self.treeview.add_node(TreeViewButton(text='Rule ' + str(i), on_press=self.edit_node), county_label)
# add conditions children to rule
for condition in rule:
self.treeview.add_node(TreeViewButton(text=condition, on_press=self.edit_node), rule_label)
#Clock.schedule_once(self.error_popup.dismiss())
#somehow close popup_thread
I included a kivy Clock attempt in case that is more on the right track of what I am looking for, however currently it will just open the popup and never populate the tree. I am new to GUI programming and event callbacks, so any help is greatly appreciated.
I tried keeping the code short, if more is needed please let me know.
I built an app which does something similar to what you're doing (different computation, but as you said the point was it was time-consuming and you want to thread a popup that shows the app isn't crashed - it's just crankin' the numbers). What ended up working for me was to set up a button to execute a dummy function which toggles both the popup and the calculation. Run the popup first and then thread the calculation through the 'from threading import Thread' module to execute the computation on a separate thread.
Here's a working example. It's just sleeping for 5 seconds but you can stick your computation into that function and it should work just fine. What it does is opens the popup before the computation and closes the popup when the calculation is done. Also, you can stick a 'Loading.gif' file into the folder and it'll import that as your loading gif if you want to use something other than what kivy pulls up (which is essentially a loading gif for loading your Loading.gif which isn't loading because it's not there... haha). Also added an 'ABORT' button if your user gets tired of waiting.
Finally just as a side note, I've had difficulties getting the .kv file to build into the pyinstaller application bundeler, so just as a heads up, using the builder.load_string(KV) function is a good alternative for that.
from threading import Thread
from sys import exit
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.lang import Builder
KV = '''
<Pop>:
id:pop
title: ''
auto_dismiss: False
padding: 10
spacing: 10
BoxLayout:
BoxLayout:
padding: 10
spacing: 10
orientation: 'vertical'
Label:
font_size: 22
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
text: "Process is currently running."
Label:
id: error_msg
size_hint_x: 0.3
text: ''
BoxLayout:
orientation: 'vertical'
Button:
background_color: (1,0,0,1)
text: "ABORT"
on_press: root.sysex()
AsyncImage:
source: 'Loading.gif'
<MetaLevel>:
rows: 1
cols: 1
Button:
text: 'RUN'
on_release: root.dummy()
'''
Builder.load_string(KV)
class MetaLevel(GridLayout):
def dummy(self, *args):
App.get_running_app().pop.open()
Thread(target=self.calculate, args=(args,), daemon=True).start()
def calculate(self, *args):
import time
time.sleep(5)
App.get_running_app().pop.dismiss()
class Pop(Popup):
def sysex(self):
exit()
class Cruncher(App):
def build(self):
self.pop = Pop()
return MetaLevel()
if __name__ == "__main__":
Cruncher().run()
Were you able to get this sorted?
I think it works if you use the thread for populating the tree rather than using it for showing the popup. After populating the tree, in the same thread you can close the pop up using Popup.dismiss()
main.py file
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
import time, threading
class popupTestApp(App):
def waitSec(self):
time.sleep(5)
self.p.dismiss()
def popUpFunc(self):
self.p = Popup(title='Test Popup', content=Label(text='This is a test'), size_hint=(None,None), size=(400,400))
self.p.open()
popUpThread = threading.Thread(target=self.waitSec)
popUpThread.start()
if __name__ == '__main__':
popupTestApp().run()
popuptest.kv file
BoxLayout:
BoxLayout:
id:LeftPane
Button:
id:MyButton
text:'Pop it up!'
on_release:app.popUpFunc()
BoxLayout:
id:RightPane
Label:
text: 'Another Pane'
Take a look at the below link where this is explained well.
Building a simple progress bar or loading animation in Kivy

Kivy's ScreenManager and Popups don't want to work together

as stated in the title - I'm stuck. I've been playing with the code around and everything works as long as I keep ScreenManager and Popup separate. Once combined - they refuse to cooperate. Anyway, here is the simple app that shows the problem I'm having.
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.popup import Popup
from kivy.uix.screenmanager import Screen, ScreenManager
class First(GridLayout,Screen):
def show_popup(self):
Popp().open()
pass
class Second(Screen):
pass
class Popp(Popup):
pass
class ScreenManagement(ScreenManager):
pass
app = Builder.load_file("main.kv")
class MainApp(App):
def build(self):
return app
if __name__ == "__main__":
MainApp().run()
And main.kv file
ScreenManagement:
First:
Second:
<First>:
name:"First"
rows: 2
Button:
text: "FirstButton"
on_release: app.root.current = "Second"
Button:
text: "Show popup"
on_release: root.show_popup()
<Second>:
name:"Second"
Button:
text: "BUTTON"
on_release: app.root.current = "First"
<Popp>:
title: "testing"
text: "Hello world"
size_hint: None,None
size: 400,400
auto_dismiss: False
Button:
text: "Okay"
on_press: root.dismiss()
App starts, first and second screen are working but when trying to get popup up I end up with:
kivy.uix.popup.PopupException: Popup can have only one widget as content
Somehow Screen is seen as a widget inside of Popp? Or am I terribly misinterpreting kivy docs?
It's a bug with loading kv file, it should throw an exception in this case.
What you are doing in the code is loading the kv file twice, what causes some weird behavior. Just delete the Builder.load_file(..) and it will work. The file is going to be loaded automatically.
Also, never do double subclassing of widgets like class First(GridLayout, Screen) as it might lead to some problems. Instead, create a grid layout inside the screen.
Put the elements in the Popup inside a layout, for example: Boxlayout.
Here's what I mean:
<Popp>:
title: "testing"
BoxLayout:
orientation: 'vertical'
size_hint: None,None
size: 400,400
Label:
text: "Hello world"
Button:
text: "Okay"
on_press: root.dismiss()
I have same problem with using kivy Builder.load_file and Popup, they dont work together.
the solution is simple, build popup in python code side. this is a loading popup example:
python:
from kivy.app import App
from kivy.uix.popup import Popup
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.popup import Popup
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
import time, threading
buildKV = Builder.load_file("example.kv")
class ExampleApp(App):
def show_popup(self):
content = BoxLayout(orientation= "vertical")
image=Image(source= 'files/loading.gif', anim_delay= 0)
label=Label(text= 'Model is Running.\nBe Patient Please.')
content.add_widget(image)
content.add_widget(label)
self.popup = Popup(title='Model is Running.',
size_hint=(.250, .785),
content=content, auto_dismiss=False)
self.popup.open()
def process_button_click(self):
# Open the pop up
self.show_popup()
# Call some method that may take a while to run.
# I'm using a thread to simulate this
mythread = threading.Thread(target=self.something_that_takes_5_seconds_to_run)
mythread.start()
def something_that_takes_5_seconds_to_run(self):
thistime = time.time()
while thistime + 10 > time.time(): # 5 seconds
time.sleep(1)
# Once the long running task is done, close the pop up.
self.pop_up.dismiss()
if __name__ == "__main__":
ExampleApp().run()
kivy:
BoxLayout:
Button:
height: 40
width: 100
size_hint: (None, None)
text: 'Click Me'
on_press: app.process_button_click()

Categories