Kivy: Starting and Stopping Threads When Button is Clicked - python

I'm having trouble with the threading in Python and Kivy. I'm trying to implement threading in my kivy app. Basically, when I click a button and the screen switches to the next one, I want to launch a thread, and when I click the 'back' button, the thread should stop. It's a pretty simple task, but my current code doesn't end the thread once I pressed 'back'.
Here is my code. I'm only including the necessary parts:
KV:
<MenuScreen>:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'bgpics/blackboard.png'
FloatLayout:
Button: #THIS IS the button that should start the thread
text: "Learn"
background_normal:'bgpics/chalk1.png'
color: 1, 1, 1, 1
size_hint: .2, .2
font_name: 'fonts/SqueakyChalkSound.ttf'
font_size: '40sp'
pos_hint:{"x":.09,"y":.3}
size_hint: .4, .4
on_press: root.manager.current = 'learncategories'
<LearnScreen>:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'bgpics/categorybg.jpg'
Button: #THIS IS the button that should stop the thread
text: "BACK"
background_normal:'bgpics/yepaint.png'
color: 0, 0, 0, 1
font_size: '35sp'
font_name: 'fonts/DK Lemon Yellow Sun.otf'
pos: 50, 5
size_hint: .27, .27
on_press: root.manager.current = 'menu'
Python:
class Identifier:
def start_thread(self):
thread1=threading.Thread(target=self.serialtest)
thread1.start()
thread1.join()
def serialtest(self):
lol=1
while (lol>0):
print lol
lol+=1
def serialstop (self):
thread1.kill()
class LearnScreen(Screen):
def on_enter(self):
Identifier().start_thread()
def identify(self):
self.lol=Identifier()
def quitscreen(self):
self.identify.serialstop()

Related

New empty Widgets for every new day in calendar Kivy

I have an app where I want to save some events at a certain date. Top Button opens the calendar, "Add New Event" Button adds a new event in GridLayout.
First problem, I can't show the current date on the Top Button. I can show any date, but not the current date when the app starts.
Secondly I want for every new date (or new day), an empty GridLayout, where I can add new events, or to complete the old ones, and maybe later on to save all that stuff.
Thanks a lot!!
Python code:
from kivymd.app import MDApp
from kivy.uix.boxlayout import BoxLayout
from kivymd.uix.picker import MDDatePicker
class MyBox(BoxLayout):
def on_save(self, instance, value, date_range):
MDApp.get_running_app().root.ids.date.text = str(value)
def show_date(self):
date = MDDatePicker()
date.bind(on_save= self.on_save)
date.open()
def add_item(self):
MDApp.get_running_app().root.ids.grd_id.add_widget(EventTemplate())
class EventTemplate(BoxLayout):
pass
class MyAppApp(MDApp):
pass
MyAppApp().run()
KV code:
MyBox:
<MyBox>:
orientation: "vertical"
Button:
id: date
size_hint: 1, 0.2
on_release: root.show_date()
ScrollView:
canvas.before:
Color:
rgba: 0, 0, 1, 1
Rectangle:
pos: self.pos
size: self.size
GridLayout:
size_hint: 1, None
height: self.minimum_height
id: grd_id
spacing: 10
cols: 1
EventTemplate:
EventTemplate:
Button:
on_release: root.add_item()
size_hint: 1, 0.2
text: "Add New Event"
<EventTemplate>:
size_hint_y: None
spacing: 1
CheckBox:
size_hint: 0.1, 1
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: "Some Event"
canvas.before:
Color:
rgba: 0, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
You can add an on_start() method to your MyAppApp:
def on_start(self):
self.root.ids.date.text = str(datetime.date.today())

How to change the text on different labels from the same TextInput field?

In my program I have a button that adds a new box. When you press a button in this new box it adds a new box, and so on. In each box I have a label, and I want to be able to change this text from the same textinput field. But I dont want the same text in each box, so I want to select the box, write the text, and then press a button so that the text is passed from the input field to that specific box/label. I have removed everything else from my app, so I will show you a complete code so you can try the program to understand what I mean.
I have tried to use a button on the main widget, but then I dont know how to choose which box that should be updated. I have also tried to use a button in the box (called "R" in the code), but then I only gets an error.
If i use the code that is commented out i get this error:
"AttributeError: 'super' object has no attribute '__getattr__'"
I would really appreciate some help! Thanks a lot.
This is the python file:
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
class Home(FloatLayout):
def first(self, *args):
a = box()
self.ids.frame.add_widget(a)
b = a.ids.btn3
b.bind(on_press=self.second)
c = a.ids.btn1
c.bind(on_press=self.textedit)
def second(self, *args):
d = box()
e = d.ids.mains
e.pos_hint={"right": .5, "top": .7}
self.ids.frame.add_widget(d)
f = d.ids.btn3
f.bind(on_press=self.third)
g = d.ids.btn1
g.bind(on_press=self.textedit)
def third(self, *args):
h = box()
i = h.ids.mains
i.pos_hint = {"right": .3, "top": .9}
self.ids.frame.add_widget(h)
j = h.ids.btn1
j.bind(on_press=self.textedit)
def textedit(self, *args):
print("Hello")
#k = self.ids.tinput.text
#l = self.ids.lab
#l.text = k
def textedit2(self, *args):
print("hei")
#This is the submitbutton on the main widget
class HiApp(App):
def build(self):
return Home()
class box(FloatLayout):
pass
if __name__ == "__main__":
HiApp().run()
This is the .kv file
<Home>:
FloatLayout:
id: frame
size_hint_y: 1
pos_hint:{"right":1,"top":1}
canvas.before:
Color:
rgba: (1, 1, 1, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
id: boks
orientation: "vertical"
size_hint_x: .20
size_hint_y: .15
pos_hint:{"right":1,"top":1}
canvas.before:
Color:
rgba: (0, 1, 0, 1)
Rectangle:
size: self.size
pos: self.pos
TextInput:
id: tinput
hint_text: "Frome here"
Button:
id: t_sub
text: "Submit"
on_press: root.textedit2()
Button:
id: start
text: "Start"
font_size: 20
color: 0, 0, 0, 1
background_normal: ''
background_color: .88, .88, .88, 1
size_hint: .1, .1
pos_hint:{"right":.5,"top":.35}
on_press: root.first()
<box>:
BoxLayout:
id: mains
orientation: "vertical"
size_hint_x: .18
size_hint_y: .13
pos_hint:{"right":.3,"top":.5}
canvas.before:
Color:
rgba: (.20, .05, .0, 1)
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
id: c1
size_hint_y: .25
pos_hint:{"left":.1,"top":.5}
GridLayout:
rows: 1
cols: 3
padding: 4
spacing: 4
Button:
id: btn1
text: "R"
font_size: 30
color: 0, 0, 0, 1
background_normal: ''
background_color: .88, .88, .88, 1
size_hint: .3, .3
pos_hint:{"left":.5,"top":.5}
on_press:
Button:
id: btn2
text: "-"
font_size: 30
color: 1, 0, 0, 1
background_normal: ''
background_color: .88, .88, .88, 1
size_hint: .3, .3
pos_hint:{"left":.5,"top":.5}
on_press:
Button:
id: btn3
text: "+"
font_size: 30
color: 0, 1, 0, 1
background_normal: ''
background_color: .88, .88, .88, 1
size_hint: .3, .3
pos_hint:{"left":.5,"top":.5}
on_press:
GridLayout:
rows: 1
cols: 2
padding: 4
spacing: 4
Label:
id: lab
text: "Text here"
TextInput:
hint_text: "0"
Problem
I want to write the text in the inputfield with the id: tinput
(upper left corner) and then press the button (R) so that the text
goes from the inputfield to the label.
Solution
The solution to is use the following keywords in kv file:
app - always refers to the instance of your application
root - refers to the base widget/template in the current rule
Snippets - kv file
Button:
id: btn1
text: "R"
font_size: 30
color: 0, 0, 0, 1
background_normal: ''
background_color: .88, .88, .88, 1
size_hint: .3, .3
pos_hint:{"left":.5,"top":.5}
on_press:
lab.text = app.root.ids.tinput.text
Output

Handling keyboard input in regard to its source in Kivy

I am currently experimenting a bit with Kivy and added a TextInput to my GUI.
I now want to keep features such as deleting the last character when pressing backspace and so on but also want Kivy to execute a method when a new letter is entered to my textInput.
How would I do that? I tried the following code which works in terms of calling said function but doesn't keep the backspace and delete functionality.
Code:
KV
#:kivy 1.9.0
<PageLayout>:
GridLayout:
searchterm: searchterm
cols: 2
rows: 2
spacing: 5
padding: 5
Button:
text: "maybe a listview of latest traces here"
size_hint: .2,.5
BoxLayout:
orientation:'vertical'
TextInput:
id: searchterm
multiLine: False
hint_text: "Search for specific processes here"
keyboard_on_key_down: root.on_search
Button:
text: "room for imagination"
size_hint: 0.2, .5
Button:
text: "out of ideas"
BoxLayout:
orientation: "vertical"
Button:
text: "this is a new page for additional information"
Label:
halign: 'center'
valign: 'center'
size_hint: None, .025
width: self.parent.width
text: "About:"
color: 0,0,0,1 #textcolor
canvas.before:
Color:
rgba: 0.5,0.5,0.5,1
Rectangle:
pos: self.pos
size: self.size
Label:
halign: 'center'
valign: 'center'
size_hint: None, .025
width: self.parent.width
text: "This tool was written by me"
color: 0,0,0,1 #textcolor
canvas.before:
Color:
rgba: 0.5,0.5,0.5,1
Rectangle:
pos: self.pos
size: self.size
Label:
halign: 'center'
valign: 'center'
size_hint: None, .025
width: self.parent.width
text: "It is only meant for evaluation and testing purposes."
color: 0,0,0,1 #textcolor
canvas.before:
Color:
rgba: 0.5,0.5,0.5,1
Rectangle:
pos: self.pos
size: self.size
Python
import kivy
from kivy.app import App
from kivy.uix.pagelayout import PageLayout
from kivy.config import Config
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
#WINDOW ATTRIBUTES
Config.set('graphics','resizable','1')
Config.set('graphics','width','1000')
Config.set('graphics','height','800')
class Visualizer(PageLayout):
searchterm = ObjectProperty(None)
def on_search(self,*args,**kwargs):
print("Test")
print(*args)
print(**kwargs)
class VisualizerApp(App):
def build(self):
self.title = "MQTT IDS Visualizer. Author: Daniel"
return Visualizer()
GLAPP = VisualizerApp()
GLAPP.run()
You need to call the super for keyboard_on_key_down. I believe the easiest way to do that is to define a subclass of TextInput like so:
class MyTextInput(TextInput):
def keyboard_on_key_down(self, window, keycode, text, modifiers):
App.get_running_app().root.on_search( window, keycode, text, modifiers) # calls your `on_search()` method
return super(MyTextInput, self).keyboard_on_key_down(window, keycode, text, modifiers)
Then replace TextInput with MyTextInput in your kv file.

Black screen on game launch

I'm trying to start a game when I click on "start game". Menu screen is removed and game launches (I can hear the sounds and then being killed by an enemy) but can't see a thing. All I get is a black screen. It's like the game is launched in the background.
What am I doing wrong ?
updated kv file:
<Game_Screen>:
name: 'game_screen'
on_enter: app.launch_game()
<Game_Menu>:
FloatLayout:
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'SPACE SHOOTER'
font_size: 40
font_name: "fonts/Alexis Italic.ttf"
color: 0, 1, 0, 1
pos_hint: {'center_x': 0.5, 'center_y': 1.7}
BoxLayout:
canvas.before:
Rectangle:
pos: self.pos
size: self.size
size: self.parent.size
pos: self.parent.pos
orientation: 'vertical'
Label:
text: 'menu'
font_name: "fonts/Alexis Bold.ttf"
font_size: 30
color: 0, 1, 0, 1
Button:
text: 'start game'
on_press: app.root.current = 'game_screen'
Button:
text: 'highest score'
on_release:
Button:
text: 'credits'
on_release: app.root.current = 'credits'
updated Python code:
class Game(Widget):
# works fine on its own
class Game_Menu(Screen):
pass
class Game_Screen(Screen):
pass
class Menu_UI(Widget):
pass
class Credits_Screen(Screen):
pass
class GameApp(App):
sm = ScreenManager()
sm.add_widget(Game_Menu(name='menu'))
sm.add_widget(Credits_Screen(name='credits'))
sm.add_widget(Game_Screen(name='game_screen'))
def launch_game(self):
game = Game()
Window.size = game.size
engines1.loop = True
engines1.play()
return game
def build(self):
return self.sm
if __name__ == '__main__':
GameApp().run()
Problem
Programming Guide » Events and Properties » Main loop
In Kivy applications, avoid long/infinite loops or sleeping. The
program will never exit your loop, preventing Kivy from doing all of
the other things that need doing. As a result, all you’ll see is a
black window which you won’t be able to interact with.
In your kv file, you have FloatLayout:, Kivy will consider that as a root widget.
Rule context
The root rule is declared by declaring the class of your root widget,
without any indentation, followed by : and will be set as the root
attribute of the App instance.
Solution
Use Clock schedule functions e.g. schedule_interval(), schedule_once(), or create_trigger() in replacement of long/infinite loops or sleeping.
Move codes for sm immediately after GameApp into the build method.
Indent all the codes after <Game_Menu>
Snippets - main.py
class GameApp(App):
def launch_game(self):
game = Game()
Window.size = game.size
engines1.loop = True
engines1.play()
return game
def build(self):
self.sm = ScreenManager()
self.sm.add_widget(Game_Menu(name='menu'))
self.sm.add_widget(Credits_Screen(name='credits'))
self.sm.add_widget(Game_Screen(name='game_screen'))
return self.sm
Snippets - game.kv
#:kivy 1.10.0
<Game_Screen>:
name: 'game_screen'
on_enter: app.launch_game()
<Game_Menu>:
FloatLayout:
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
size_hint: None, None
canvas.before:
Color:
rgba: 1, 0, 0, 1
Rectangle:
pos: self.pos
size: self.size
Label:
text: 'SPACE SHOOTER'
font_size: 40
font_name: "fonts/alexisv3ital.ttf" # "fonts/Alexis Italic.ttf"
color: 0, 1, 0, 1
pos_hint: {'center_x': 0.5, 'center_y': 1.7}
Output

how to close a Screen without interrupting the program in Kivy

I am beginner with respect to programming already resorted to previously forum which received help for simple issues.
I am currently looking for a way to close a screen without interrupting my program, I am using the "thread" to continue executing commands while the program is in the loop.
MAIN
from kivy.app import App
from kivy.uix.screenmanager import Screen
from kivy.properties import ObjectProperty
from kivy.core.audio import SoundLoader
from time import sleep
import thread
class Display(Screen):
myslider = ObjectProperty(None)
def get_number(self):
occupancy = format(self.myslider.value)
print "value = ",occupancy
def display_screen():
thread.start_new_thread(myApp().run, ())
def remove_screen():
Screen.disabled()
def ring():
sound = SoundLoader.load('ring.wav')
if sound:
sound.play()
class myApp(App):
def build(self):
return Display()
if __name__ == '__main__':
ring()
sleep(2)
display_screen()
sleep(7)
remove_screen()
KV file
#:import random random.random
<Display>:
orientation: 'vertical'
myslider: slider
canvas:
Color:
rgb: 1, 1, 1
Rectangle:
source: 'data/images/background.jpg'
size: self.size
BoxLayout:
padding: 10
spacing: 10
size_hint: 1, None
pos_hint: {'top': 1}
height: 44
Image:
size_hint: None, None
size: 24, 24
source: 'data/logo/kivy-icon-24.png'
Label:
height: 24
text_size: self.size
color: (1, 1, 1, .8)
text: 'Kivy 1.9.0.'
valign: 'middle'
GridLayout:
cols: 2
Label:
text: 'Please enter \nthe number of occupants?'
bold: True
font_name: 'data/fonts/DejaVuSans.ttf'
font_size: 22
halign: 'center'
Slider:
id: slider
min: 0.0
max: 15.0
value: 1.0
step: 1.0
orientation: "horizontal"
width: "38dp"
Label
text: ''
Label
text: '{}'.format(slider.value)
halign: 'center'
valign: 'top'
bold: True
text_size: self.size
font_size: 18
Button:
text: 'Enter'
size_hint_y: None
height: '50sp'
on_release: root.get_number()
I left remove_screen function because it was what I found so far in my research. but not work.

Categories