I am writing code for a heating control system.
I just want to be able to change the label texts FROM WITHIN PYTHON.
By that I mean, NOT in the GUI code, but somewhere else in the main.
Here is my MWE:
import time
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
import multiprocessing
from kivy.properties import StringProperty
class Data (Widget):
top = StringProperty('hiii')
def __init__(self, **kwargs):
super(Widget, self).__init__(**kwargs)
global mydata
mydata=Data()
class myw (BoxLayout):
def __init__(self, **kwargs):
super(myw, self).__init__(**kwargs)
VERT = BoxLayout(orientation='vertical')
o = Label(text='Oben: ',
font_size=120)
m = Label(text='Mitte: ',
font_size=120)
u = Label(text='Unten: ',
font_size=120)
a = Label(text='Aussen: ',
font_size=120)
mydata.bind(top=o.setter('text'))
VERT.add_widget(o)
VERT.add_widget(m)
VERT.add_widget(u)
VERT.add_widget(a)
onoff = Button(text='Ein',
font_size=120,
size_hint=(0.3, 1))
self.add_widget(VERT)
self.add_widget(onoff)
class TutorialApp(App):
def build(self):
return myw()
if __name__ == "__main__":
try:
global myapp
myapp=TutorialApp()
app_runner=multiprocessing.Process(target=myapp.run)
app_runner.start()
time.sleep(3)
mydata.top='new value assigned'
print (mydata.top)
time.sleep(5)
app_runner.terminate()
except Exception as e:
print ('error occured', e)
I deliberately declared the variable 'mydata' outside the kivy code, such that i can access it from elsewhere in the code (not shown here).
Using threading instead of multiprocessing solved the problem.
So instead of
app_runner=multiprocessing.Process(target=myapp.run)
it now reads:
app_runner=threading.Thread(target=myapp.run)
Related
I am new to Kivy and GUIs in general and I am having a hard time understanding some simple concepts of Kivy callbacks logic. I want to make a simple app to read a CSV and plot a graph from it, so I want the widget that opens the file to trigger a function that adds a FigureCanvaKivyAgg widget that shows the figure.
On my source code I disabled the plotting from the CSV readed as it does not add to the issue at hand. You will see that I tried different ways to trigger the callback, but I only accomplish to trigger the custom event, it does not communicate with the callback nor add the FigureCanva Widget to the Layout.
import pandas as pd
import matplotlib.pyplot as plt
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.button import Button
from kivy.uix.filechooser import FileChooserIconView
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
from shark_plot import shark_plot
import os
csv = {'x':[1,2,3,4,5,6],'y':[2,4,6,8,6,12]}
class MyWidget(BoxLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.register_event_type('on_event')
container = BoxLayout(orientation='vertical')
filechooser = FileChooserIconView()
filechooser.bind(on_selection=lambda x: self.selected(filechooser.selection))
print(filechooser.selection,filechooser.path)
open_btn = Button(text='open', size_hint=(1, .2))
open_btn.bind(on_release=lambda x: self.open(filechooser.path, filechooser.selection))
box = BoxLayout(size_hint=(1, .5))
box.bind(on_event= self.action)
# if len(filechooser.path[0])>0:
# # box.add_widget(FigureCanvasKivyAgg(plt.gcf()))
container.add_widget(filechooser)
container.add_widget(open_btn)
container.add_widget(box)
self.add_widget(container)
def on_event(self):
print("Event triggered?")
plt.show()
canva = (FigureCanvasKivyAgg(plt.gcf()))
return canva
def action(self):
print('Action executed??')
canva = (FigureCanvasKivyAgg(plt.gcf()))
self.cb.ask_update()
return canva
def open(self, path, filename):
print(filename)
if len(filename) > 0:
selected_path = os.path.join(path, filename[0])
print(selected_path)
df=pd.read_csv(selected_path)
# fig = shark_plot(df)
fig = plt.plot(csv['x'],csv['y'])
g=self.dispatch('on_event')
print(type(df))
# print(f.read())
return fig
def selected(self, filename):
print("selected: %s" % filename[0])
def on_event_callback():
print("Action Triggered??")
canva = (FigureCanvasKivyAgg(plt.gcf()))
subbox = BoxLayout()
subbox.add_widget(canva)
return subbox
class MyApp(App):
def build(self):
return MyWidget()
if __name__ == '__main__':
MyApp().run()
Hope that the question is clear, it is my first time making such here :)
Please reach out for any clarifications.
on_event() will run via the dispatch() statement, but there is no connection from on_event() to the add_widget() statement in on_event_callback(), so that never happens. You can either put add_widget() directly in on_event(), or, if you want a separate function, then call it directly from open() without using the kivy register and dispatch().
def on_event(self):
canva = FigureCanvasKivyAgg(plt.gcf())
self.add_widget(canva)
def on_event_callback(self,canva):
self.add_widget(canva)
def open(self, path, filename):
# open data files here etc to load self.csv
plt.plot(self.csv['x'],self.csv['y'])
self.dispatch('on_event')
canva2 = FigureCanvasKivyAgg(plt.gcf())
self.on_event_callback(canva2)
i am new in the Kivy Topic and i have got a simple question (i think).
With the function "zufall" i create a random number.
This number should update every 2 seconds in the label.
But when i am running the code, the error "Label.text accept only str" occurs.
But from my opinion i made the "random_number" to a string. Or is there another problem, with my thinking?
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
import random
from kivy.clock import Clock
from kivy.properties import StringProperty
class ConnectPage(GridLayout):
# runs on initialization
def zufall(self, *args):
random_number = random.randrange(10)
random_number = str(random_number)
print(random_number)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2 # used for our grid
self.add_widget(Label(text='OEE'))
self.add_widget(Label(text=self.zufall))
class EpicApp(App):
def build(self):
t = ConnectPage()
Clock.schedule_interval(t.zufall, 2)
return t
if __name__ == "__main__":
EpicApp().run()
Can someone of you give me a hint?
Firstly you have to return your random number from your zufall function, and call that function from your __init__ like this:
# runs on initialization
def zufall(self, *args):
random_number = random.randrange(10)
random_number = str(random_number)
return random_number
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.cols = 2 # used for our grid
self.add_widget(Label(text='OEE'))
self.add_widget(Label(text=self.zufall()))
I am trying to bind a widgets' property to that of its child. I have used the "setter", as pointed out by the documentation. The parent's property is however never changed and its value remains as in the beginning (None).
Any help is appreciated!
Here is my code example:
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.label import Label
MESSAGE = "Checkbox active? '{}'"
class MainScreen(BoxLayout):
child_state = ObjectProperty()
def __init__(self, *args, **kwargs):
super().__init__(orientation="vertical", *args, **kwargs)
self.checkbox = CheckBox()
# Why doesn't this work?
self.bind(child_state=self.checkbox.setter("active"))
self.label = Label(text=MESSAGE.format(self.child_state))
self.button = Button(text="Print properties",
on_release=self.print_properties)
self.add_widget(self.checkbox)
self.add_widget(self.label)
self.add_widget(self.button)
def on_child_state(self, *args):
self.label.text = MESSAGE.format(self.child_state) # Never updated
def print_properties(self, *args):
print(f"child_state property {self.child_state}") # Always None
print(f"checkbox property {self.checkbox.active}") # Changes between True/False
print()
class MyApp(App):
def build(self):
return MainScreen()
if __name__ == '__main__':
MyApp().run()
I am currently trying to develop a test app by kivy. I am using python 2.7 . My code below returns
AttributeError: 'MainScreen' object has no attribute 'iptHpIdx'.
Could anyone please help me on this error? I just suspected that usage of self is wrong but I spent too long time to figure it out and still I have no clue on the reason why this occurs.
Thank you so much for your help in advance!
import kivy
kivy.require('1.8.0')
import numpy as np
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty
import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivyagg')
from matplotlib import pyplot as plt
from kivy.garden.matplotlib import FigureCanvasKivyAgg
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainScreen(Screen):
screenmanager = ObjectProperty()
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
iptHpIdx = TextInput(text = '', multiline=False, font_size=50)
btnToPoint = Button(text='input')
btnToPoint.bind(on_press=self.IptAct)
layout = BoxLayout(orientation='vertical') # syntax(padding=10, orientation='vertical')
layout.add_widget(iptHpIdx)
layout.add_widget(btnToPoint)
layout.add_widget(canvas)
self.add_widget(layout)
def IptAct(self, btn):
global HpHist
self.btnToPoint.text = 'Your input ' + self.iptHpIdx.text
try:
val = int(self.iptHpIdx.text)
HpHist.append(self.iptHpIdx.text)
print 'HpHistAf', HpHist
except ValueError:
print "That's not an int!"
pass
print HpHist
plt.clf()
plt.plot(HpHist)
canvas.draw_idle()
class Test(App):
def build(self):
sm = ScreenManager()
sc1 = MainScreen(name='MainPage')
sm.add_widget(sc1)
return sm
if __name__ == '__main__':
HpHist = []
(fig, axe) = plt.subplots()
canvas = fig.canvas
Test().run()}
You are referencing self.iptHpIdx in IptAct() function but you never created that on your __init__(), you need to create it as self.iptHpIdx. So change your __init__() to:
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
self.iptHpIdx = TextInput(text = '', multiline=False, font_size=50)
self.btnToPoint = Button(text='input')
self.btnToPoint.bind(on_press=self.IptAct)
layout = BoxLayout(orientation='vertical') # syntax(padding=10, orientation='vertical')
layout.add_widget(self.iptHpIdx)
layout.add_widget(self.btnToPoint)
layout.add_widget(canvas)
self.add_widget(layout)
You have to declare ObjectProperty and hook it up the child widgets. Please refer to the example below for details.
Snippets
class MainScreen(Screen):
ms_iptHpIdx = ObjectProperty(None)
ms_btnToPoint = ObjectProperty(None)
def __init__(self, **kwargs):
...
self.ms_iptHpIdx = iptHpIdx
self.ms_btnToPoint = btnToPoint
...
def IptAct(self, btn):
global HpHist
self.ms_btnToPoint.text = 'Your input ' + self.ms_iptHpIdx.text
try:
val = int(self.ms_iptHpIdx.text)
HpHist.append(self.ms_iptHpIdx.text)
Example
main.py
import kivy
kivy.require('1.10.0')
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.textinput import TextInput
from kivy.properties import ObjectProperty
import numpy as np
import matplotlib
matplotlib.use('module://kivy.garden.matplotlib.backend_kivyagg')
from matplotlib import pyplot as plt
from kivy.garden.matplotlib import FigureCanvasKivyAgg
class MainScreen(Screen):
ms_iptHpIdx = ObjectProperty(None)
ms_btnToPoint = ObjectProperty(None)
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
iptHpIdx = TextInput(text='', multiline=False, font_size=50)
btnToPoint = Button(text='input')
self.ms_iptHpIdx = iptHpIdx
self.ms_btnToPoint = btnToPoint
btnToPoint.bind(on_press=self.IptAct)
layout = BoxLayout(orientation='vertical') # syntax(padding=10, orientation='vertical')
layout.add_widget(iptHpIdx)
layout.add_widget(btnToPoint)
layout.add_widget(canvas)
self.add_widget(layout)
def IptAct(self, btn):
global HpHist
self.ms_btnToPoint.text = 'Your input ' + self.ms_iptHpIdx.text
try:
val = int(self.ms_iptHpIdx.text)
HpHist.append(self.ms_iptHpIdx.text)
print('HpHistAf', HpHist)
except ValueError:
print("That's not an int!")
print(HpHist)
plt.clf()
plt.plot(HpHist)
canvas.draw_idle()
class TestKivyMatplotlib(App):
def build(self):
sm = ScreenManager()
sm.add_widget(MainScreen(name='MainPage'))
return sm
if __name__ == '__main__':
HpHist = []
(fig, axe) = plt.subplots()
canvas = fig.canvas
TestKivyMatplotlib().run()
Output
I am trying to build an app that downloads a file, whose progress can be tracked on a kivy app.
I have looked at the example here and here for the download progress.
This is my code:
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.popup import Popup
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.clock import Clock
import urllib
URL = "http://s9.videozoo.me/S/saenai_heroine_no_sodatekata_flat_-_11.mp4?st=Ow7pwXbRt6vPWE-kr5Sn1A&e=1498847899&start=0"
class PopupBox(Popup):
pop_up_text = ObjectProperty()
def update_pop_up_text(self, p_message):
self.pop_up_text.text = p_message
class MyApp(App):
# layout
def show_popup(self):
self.pop_up = Factory.PopupBox()
self.pop_up.update_pop_up_text('Running some task...')
self.pop_up.open()
def build(self):
layout = BoxLayout(padding=10, orientation='vertical')
btn1 = Button(text="OK")
btn1.bind(on_press=self.buttonClicked)
layout.add_widget(btn1)
self.lbl1 = Label(text="test")
layout.add_widget(self.lbl1)
self.txt1 = TextInput(text='', multiline=False)
layout.add_widget(self.txt1)
return layout
# button click function
def buttonClicked(self, btn):
self.lbl1.text = "You wrote " + self.txt1.text
self.show_popup()
self.download_file(URL)
self.pop_up.dismiss()
def download_file(self, url):
u = urllib.request.urlopen(url)
meta = u.info()
metaInfo = str(meta).split()
fileTotalbytes = int(metaInfo[46])
data_blocks = []
total = 0
while True:
block = u.read(1024)
data_blocks.append(block)
total += len(block)
hash = ((60 * total) // fileTotalbytes)
print("[{}{}] {}%".format('#' * hash, ' ' * (60 - hash), int(total / fileTotalbytes * 100)), end="\r")
if not len(block):
break
data = b''.join(data_blocks) # had to add b because I was joining bytes not strings
u.close()
# run app
if __name__ == "__main__":
MyApp().run()
However, I am confused as to how do I bind the download_file function with the kivy widget functionality so as to make it work. How do I appropriately modify the print function so as to make it work with the widget
If you block in the mainthread kivy can no longer update the GUI. Instead create a new thread and use the #mainthread annotation to do gui updates.
Instead of the print you have to manipulate kivy widgets. You could create a rectangle and increase the width to 100%. You could create a textinput and output your text there.