Updating Kivy's display - Part two - python

The following code displays and updates the kivy display using kv strings loaded with Builder.load_string(). It works just fine. My question is how to modify this code to define the Example class in python rather than using dynamic class defined in the kv string?
import kivy
import threading
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock,mainthread
kivy.require('1.9.0')
KV1='''
Example:
<Example#BoxLayout>:
Label:
text:'hello'
Label:
text:'world'
'''
KV2='''
Example:
<Example#BoxLayout>:
Label:
text:'hello world'
Label:
text:'good morning'
'''
#mainthread
def update(dt):
try:
vRoot = App.get_running_app().root
vRoot.clear_widgets()
Builder.unload_file('file.kv')
vRoot.add_widget(Builder.load_string(KV2,filename='file.kv'))
except BaseException as e:
print (str(e))
def loop():
Clock.schedule_once(update,5)
class MyApp(App):
def build(self):
return Builder.load_string(KV1,filename='file.kv')
vThread=threading.Thread(target=loop)
vThread.start()
if __name__ == '__main__':
MyApp().run()
Thank you

You can always implement in pure python whatever you did using kvlang. However kvlang is recommended for faster, cleaner, effort less (compared to that in pure python) designing pattern.
Here, first you can define your widget in .py then design it using kvlang as,
Builder.load_string('''
<ExampleTwo>:
Label:
text:'hello world'
Label:
text:'good morning'
''')
class ExampleTwo(BoxLayout):
pass
Or can code the whole thing using pure python (without using kvlang).
With this your modified code should look like,
import kivy
import threading
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock,mainthread
from kivy.uix.boxlayout import BoxLayout
kivy.require('1.9.0')
KV1='''
Example:
<Example#BoxLayout>:
Label:
text:'hello'
Label:
text:'world'
'''
KV2='''
Example:
<Example#BoxLayout>:
Label:
text:'hello world'
Label:
text:'good morning'
'''
Builder.load_string('''
<ExampleTwo>:
Label:
text:'hello world'
Label:
text:'good morning everyone'
''')
class ExampleTwo(BoxLayout):
pass
#mainthread
def update(dt):
try:
vRoot = App.get_running_app().root
vRoot.clear_widgets()
Builder.unload_file('file.kv')
# vRoot.add_widget(Builder.load_string(KV2,filename='file.kv'))
vRoot.add_widget(ExampleTwo())
except BaseException as e:
print (str(e))
def loop():
Clock.schedule_once(update,5)
class MyApp(App):
def build(self):
return Builder.load_string(KV1,filename='file.kv')
vThread=threading.Thread(target=loop)
vThread.start()
if __name__ == '__main__':
MyApp().run()

Related

How to combine kivy file and loops in python files?

I'm learning how to use kivy. And to get things a little more difficult, I'm using kivyMD to use themes.
I'm trying to simply create a layout in a kivy file and fill it with a python loop in the python file.
My python file, for example :
from kivy.lang import Builder
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel
from kivymd.uix.screen import MDScreen
KV="""
MyWidget:
MDBoxLayout:
orientation: 'vertical'
MDLabel:
text:"hello world !"
halign: 'center'
MDGridLayout:
id: _grid
col: 2
MDLabel:
text:"hello world !"
halign: 'center'
"""
class MyWidget(MDScreen):
def on_grid(self):
for i in range(6):
self.ids._grid.add_widget(MDLabel(text=str(i+1)))
pass
#Déclaration du moteur d'application
class MainApp(MDApp):
def build(self):
return Builder.load_string(KV)
if __name__=='__main__':
MainApp().run()
But the GridLayout stays empty.
I really search and tried other solutions with ObjectProperty() but I got even more errors. And I was unable to find a simple example, something less specific and more academic.
What is the best way to create an empty nested widget in a kv file and then fill it with a python loop at launch ?
Thank you for your help.
After a lot of researchs, I found a solution. I hope it will help someone.
'''
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.properties import ObjectProperty
from kivy.lang import Builder
KV="""
<MainScreen>:
grid: _grid
BoxLayout:
Label:
text: "Hello 1 !"
GridLayout:
id: _grid
cols:2
Label:
text: "Hello 2 !"
"""
Builder.load_string(KV)
class MainScreen(Screen):
grid = ObjectProperty()
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
for i in range(6):
self.grid.add_widget(Label(text=str(i)))
class MyApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
MyApp().run()

Issue when importing kivy.core.window and using the multiprocessing library

First of all, I'd like to clarify that I have absolutely 0 background as a software engineer and this is the first time I'm using python for something besides API's and creating excel/plots.
My issue is that I was trying to create an app using kivy and then I imported the kivy.core.window library a blank screen appears.
I've seen that this issue is fairly common when using kivy and multiprocessing, but none of the information I found fixed my problem. Result of running my code without the kivy.core.window library:
Result when adding said library:
Also, if I wait like 10 seconds the blank screen disappears and kills the count_time process.
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
import time
from datetime import datetime
import multiprocessing as mp
from multiprocessing import Process, Queue, freeze_support
Builder.load_string('''
<TestScreen>:
BoxLayout:
orientation: "vertical" # place object vertically
Label:
size_hint: 1, 1
text: 'TITLE'
font_size: 150
color: 1, 1, 1, 1
Button:
text: 'start'
on_press: root.start_mp()
Button:
text: 'stop'
on_press: root.stop_time()
''')
def count_time(x):
for i in range(x):
print('count={}'.format(i))
time.sleep(1)
class Test(App):
def build(self):
sm.add_widget(TestScreen(name='test'))
return sm
class TestScreen(Screen):
def __init__(self, **kwargs):
super(TestScreen, self).__init__(**kwargs)
def start_mp(self):
self.p1 = mp.Process(target=count_time, args=(10, ))
self.p1.start()
def stop_time(self):
pass
if __name__ == '__main__':
mp.freeze_support()
sm = ScreenManager(transition=NoTransition())
Test().run()
The problem is that when you run Process, it starts a new python proces and imports the current .py file. On most platforms, the line:
if __name__ == '__main__':
protects the Test().run() from being executed and starting another App and is enough to protect from another window being opened. However, it appears that importing kivy.core.window is enough to open another window (on Windows platform) when the original .py file is imported for multiprocessing. You can read about it here.
A really ugly work around is to protect all kivy imports (and as a result, all the kivy code) with a if __name__ == '__main__': line. Like this:
import time
import multiprocessing as mp
def count_time(x):
for i in range(x):
print('count={}'.format(i))
time.sleep(1)
if __name__ == '__main__':
mp.freeze_support()
from kivy.core.window import Window
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
Builder.load_string('''
<TestScreen>:
BoxLayout:
orientation: "vertical" # place object vertically
Label:
size_hint: 1, 1
text: 'TITLE'
font_size: 150
color: 1, 1, 1, 1
Button:
text: 'start'
on_press: root.start_mp()
Button:
text: 'stop'
on_press: root.stop_time()
''')
class Test(App):
def build(self):
sm = ScreenManager(transition=NoTransition())
sm.add_widget(TestScreen(name='test'))
return sm
class TestScreen(Screen):
def __init__(self, **kwargs):
super(TestScreen, self).__init__(**kwargs)
def start_mp(self):
self.p1 = mp.Process(target=count_time, args=(10,))
self.p1.start()
def stop_time(self):
pass
Test().run()

Referencing ids within screenmanager object

I'm trying to get my head around screenmanager, in particular referencing objects within.
I used to use this to set a value:
class Widgets(Widget)
pass
w = Widgets()
w.ids.MyTitle.text = 'something'
Now I have this:
class Widgets(Screen)
pass
class SettingsScreen(Screen)
pass
sm = ScreenManager()
sm.add_widget(Widgets(name='main'))
sm.add_widget(SettingsScreen(name='settings'))
How do I reference MyTitle now? I've tried all sorts of combos such as:
sm.ids.main.MyTitle.text =
sm.main.MyTitle.text =
sm.main.ids.MyTitle.text =
.... but not getting it! Can someone put me out of my misery? Is there an easy way of browsing through the sm object or iterating through it maybe?
EDIT: Adding minimal running version:
minimal.kv:
# File name: minimal.py
#:kivy 1.8.0
<Widgets>
Button:
id: MyTitle
text: 'hello'
<SettingsScreen>:
Button:
id: Other
text: 'settings'
minimal.py:
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App
class Widgets(Screen):
pass
class SettingsScreen(Screen):
pass
class myApp(App):
def build(self):
return sm
def on_start(self):
global sm
sm.ids['main'].ids['MyTitle'].text = 'changed' # <-- this fails
Builder.load_file("minimal.kv")
sm = ScreenManager()
sm.add_widget(Widgets(name='main'))
sm.add_widget(SettingsScreen(name='settings'))
if __name__ == '__main__':
myApp().run()
To get a screen from ScreenManager, use get_screen:
sm.get_screen('main').ids.MyTitle.text = 'changed'
Also, you may construct your app so:
kv file:
# File name: minimal.py
#:kivy 1.8.0
ScreenManager:
Widgets:
name: 'main'
SettingsScreen:
name: 'settings'
<Widgets>:
Button:
id: MyTitle
text: 'hello'
<SettingsScreen>:
Button:
id: Other
text: 'settings'
and in the python file:
sm=Builder.load_file(..)
class my12App(App):
def build(self):
return sm
def on_start(self):
self.root.get_screen('main').ids.MyTitle.text = 'changed'
According to the documentation you access an id like you would any dictionary key:
widget.ids['MyTitle']
Because ScreenManager in itself derives from Widget, and a given widget maintains a list of widgets it is aware of, you probably need something like:
sm.ids[0].ids['MyTitle'].text
However this is hard to say without a Minimal, Complete, and Verifiable Example. One thing you could do is:
for id in sm.ids: # Iterate through all widgets in ids
print(id) # Get the string representation of that widget
As a side note, this:
class Widgets(Screen)
pass
... will probably cause confusion, because you're extending Widget with Widgets (via an intermediate class Screen). OOP suggests that a subclass of a class should be a more specific form of the class. So, a Screen is a type of Widget. But Widgets is really some number of Widgets.
Unfortunately neither the code in the question, nor the one in the accepted answer worked for me, and I'm unsure as to how exactly it is supposed to work. My interpretation of it is, that OP wanted to change a property of a widget (a Button) inside of one Screen based on a callback from another Screen. The below code is a complete MWE doing that:
# File name: minimal.py
#:kivy 2.0.0
ScreenManager:
FoobarScreen:
name: 'foobar'
Widgets:
name: 'widgets'
<FoobarScreen>:
Button:
id: lalala
text: 'lalala'
<Widgets>:
Button:
id: lololo
text: 'lololo'
(minimal.kv)
import kivy
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import Screen, ScreenManager
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.app import App
class FoobarScreen(Screen):
pass
class Widgets(Screen):
pass
class myApp(App):
def build(self):
return sm
def on_start(self):
self.root.get_screen('widgets').ids.lololo.text = 'changed'
self.root.current = 'widgets'
if __name__ == '__main__':
sm=Builder.load_file('minimal.kv')
myApp().run()
(minimal.py)

Interaction between Kivy widgets in Python

I'm doing a proyect using kivy but i have a problem with the checkboxes. At first I'm trying to do the program like python coding (I know it is'nt clean, but I understand more) And i have a first screen with this coding:
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.uix.checkbox import CheckBox
class MainScreen(GridLayout):
def __init__(self,**kwargs):
e=[]
super(MainScreen, self).__init__(**kwargs)
self.cols=2
def on_checkbox_active(checkbox, value):
if value:
e.append(value)
print e
else:
print('The checkbox', checkbox, 'is inactive')
self.add_widget(Label(text='Inserta assignatures desitjades',font_size=35))
self.add_widget(Label(text=''))
ch1 = CheckBox()
self.add_widget(ch1)
self.add_widget(Label(text='Termotecnia'))
ch2 = CheckBox()
self.add_widget(ch2)
self.add_widget(Label(text='Termotecnia'))
ch3 = CheckBox()
self.add_widget(ch3)
self.add_widget(Label(text='Termotecnia'))
ch4 = CheckBox()
self.add_widget(ch4)
self.add_widget(Label(text='Termotecnia'))
b1=Button(text='Exit',background_color=[0.7,0.7,1,1],font_size=24)
self.add_widget(b1)
b2=Button(text='Next',font_size=24,font_color=[1,3,4,0],background_color=[1,2,3,6])
self.add_widget(b2)
ch1.bind(active=on_checkbox_active)
ch2.bind(active=on_checkbox_active)
b1.bind(on_press=exit)
b2.bind(on_press=reloaded)
...
class SimpleKivy(App):
def build(self):
return MainScreen()
if __name__=='__main__':
SimpleKivy().run()
I want to select two or three options for example, and save it for the next screen, like a type of selection. If anyone knows how to do it and save information for the next screen it woul help me a lot, because i have the code of the next screen for all the options, but i want to preselect in the first screen and then only use which i have selected. Also if anyone can help me, i want to know hoy to do the transition to another class (screen) when the button "Next" is pressed. I know this question are pretty simple but I'm new in kivy programming and some concepts are pretty difficult. Thanks.
What you want is accessing variables in other classes. Sometimes this can be annoying and you can do it either hard way with all __init__() and stuff, or... a simplier way comes along: it's get_running_app().
You can create a dictionary or something else, where you can store any value your other classes need to access. It's similar to using globals and it costs you less lines of code. For example in your case you could use a dictionary(or nested dictionaries, json, ...) to store for example 'checkboxes':'<names of checked ones>' and in each init you can loop over these values to make checkboxes active
Basically all you need is a = App.get_running_app() somewhere and something to access in main - App - class.
Example:
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
Builder.load_string('''
<Root>:
MainScreen:
name: 'main'
AnotherScreen:
name: 'another'
<MainScreen>:
BoxLayout:
Button:
text: 'next screen'
on_release: root.parent.current='another'
Button:
text: 'ping!'
on_release: root.ping()
<AnotherScreen>:
BoxLayout:
Button:
text: 'previous screen'
on_release: root.parent.current='main'
Button:
text: 'ping!'
on_release: root.ping()
''')
class MainScreen(Screen):
def __init__(self, **kw):
super(MainScreen, self).__init__(**kw)
self.a = App.get_running_app()
def ping(self):
print self.a.big_dict['hi']
class AnotherScreen(Screen):
def ping(self):
b = App.get_running_app()
print b.big_dict['hi']
class Root(ScreenManager):
pass
class SimpleKivy(App):
big_dict={'hi':'hi there!'}
def build(self):
return Root()
SimpleKivy().run()
You can see there's no need to call __init__(), no need to write more lines of code if you really don't need to.

How to get console output printed using kivy

I have been trying to learn how to use Kivy python and I would like to
Know how to interact with the Os console/terminal to run commands and
receive results. The tutorials I have seen so far only show how to create
widgets,buttons,etc
For example how do You get the result from running the command "uname"
displayed in kivy. With such code below. using "on press". How can I let it interact with the OS run a command and display it back in the kivy app. Is there any tutorial on creating desktop apps/utilities
from kivy.app import App
from kivy.uix.button import Button
class tutap(App):
def build(self):
return Button(text="Press here")
tutap().run()
Update:
Here is example of what Im trying to achieve.This uses the easygui module:
import subprocess
from easygui import *
msg= "what you want"
out = subprocess.check_output("uname -a",shell=True)
title = "My choice"
choices=["kernel version","nothing"]
choice=boolbox(msg,title,choices)
if choice==1:
msgbox(out)
elif choice==0:
msgbox("The End")
The simplest way I can think to do this is just make a function that writes to a file at the top of your file like this
def printlog(message):
with open('./log.txt','a') as f: f.write(message+"\n")
then in your program when ever you want the print out put simply put printlog("whatever you wanted printed!")
The file will be saved in the same folder as your program.
More of a you can theoretically open it while the program is running, but this is more useful as a postmourtum.
I don't really see the point of doing something like that but if you want you can call App.run() method in separate thread so it won't block command line.
An example using cmd module:
import logging
logging.getLogger("kivy").disabled = True
from kivy.app import App
from kivy.uix.listview import ListView
from cmd import Cmd
from threading import Thread
class MyApp(App):
def build(self):
self.lv = ListView()
return self.lv
def update(self, line):
self.lv.adapter.data.append(line)
return "list updated"
class MyCmd(Cmd, object):
def __init__(self, app, *args):
super(HelloWorld, self).__init__(*args)
self.app = app
def do_EOF(self, line):
self.app.stop()
return True
def default(self, line):
ret = self.app.update(line)
print(ret)
if __name__ == '__main__':
app = MyApp()
Thread(target=app.run).start()
MyCmd(app).cmdloop()
Here I how I went about getting console command output.
The python code first:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.popup import Popup
from kivy.properties import ObjectProperty
from kivy.uix.label import Label
import subprocess
class shellcommand(BoxLayout):
first=ObjectProperty()
second=ObjectProperty()
third=ObjectProperty()
def uname(self):
v=subprocess.check_output("uname -a",shell=True)
result=Popup(title="RESULT",content=Label(text="kernel is\n" + v))
result.open()
def date(self):
d=subprocess.check_output("date",shell=True)
res=Popup(title="DATE",content=Label(text="the date today is\n" + d))
res.open()
def last(self):
last=subprocess.check_output("w",shell=True)
ls=Popup(title="LOGIN",content=Label(text="logged in \n" + last))
ls.open()
class shellApp(App):
def build(self):
return shellcommand()
shellApp().run()
And then the kivy file named shellapp.kv
<shellcommand>:
orientation: "vertical"
first:one
second:two
third:three
canvas:
Rectangle:
source: "snaps.png" #location of any picture
pos: self.pos
size: self.size
BoxLayout:
orientation: "horizontal"
Button:
id:one
text: "UNAME"
background_color: 0,0,0,1
font_size:32
size_hint:1,None
on_press: root.uname()
Button:
id:two
text: "DATE"
background_color: 1,1.5,0,1
font_size:32
size_hint:1,None
on_press: root.date()
Button:
id: three
text: "LOGGED IN"
background_color: 1,0,0,1
font_size:32
size_hint: 1,None
on_press: root.last()
If there is a way to improve this code please let Me know how to.Thanks

Categories

Resources