Kivy label does not update until after next function is called - python

I am working on a doorbell project using a raspberry pi 3 and the 7inch touchscreen display. I am using kivy to power the GUI however have run into the following issue I cannot seem to solve.
The kivy app loads on a button press and gives the user options for authentication (e.g rfid, keypad etc) On selecting an option it takes the user to another screen telling them to input their pin in the case of the keypad option.
This is just the snippet for the keypad screen:
class KeypadScreen(Screen):
keypadTextTest = StringProperty("Please Input 4 Digit Passcode")
def Decision(self, *args):
keypadOutput = FourDigitCodeCheck()
if keypadOutput == True:
self.change_text()
Clock.schedule_interval(self.change_text, 0.1)
self.change_text()
doorOpen()
return
def change_text(self, *args):
self.keypadTextTest = "Door Open"
When the user inputs their pin and the code returns True (Ie it is the correct pin) I wish for the label to change to say "Door Open" and then run the doorOpen function (seen below) which for now just turns an LED on and off.
def doorOpen():
print ("Door Open : Please Enter")
GreenLED("ON")
sleep(5)
GreenLED("OFF")
return
Currently however the doorOpen function is called before the label changes meaning that the LED turns on, then off 5 seconds later and then the label changes.
I have tried various different methods of updating the label text and have even tried adding in different alterations afterwards to try and force an update before the doorOpen function is ran however to no avail.
Any input would be greatly appreciated as has been a tedious afternoon. Below is the kivy lang snippet for the keypad screen encase something is wrong there:
<KeypadScreen>:
on_enter: root.Decision()
BoxLayout:
orientation: 'vertical'
BoxLayout:
orientation: 'vertical'
Label:
id: keypadtext
text: root.keypadTextTest
font_size: 50
Pastebin of full python script: https://pastebin.com/K2CnnmVB
Pastebin of full Kivy script: https://pastebin.com/9mgZFxyx

Related

Python Kivy library adding widgets

For example we created with kivy language simple BoxLayout1 that contains button1 and another BoxLayout2. When you click button1 it adds a new button in BoxLayout2 but it's written in python.
Is it possible to acces existing layout in kv language, in python code? Or only solution is writting whole window in python?
I couldn't find any info in kivy docs, maybe I've just missed something.
EDIT:
I've something like this
Kv:
<CreateWindow>:
BoxLayout:
Button:
text: "test"
on_release: root.press()
BoxLayout:
Label:
text: "Label"
Python:
class CreateWindow(Screen):
def press(self):
I want to add a new button near Label by activating press function
in python will be like this
class CreateWindow(Screen):
def press(self):
# the first box is the first child in the children list of the CreateWindow widget so
box1=self.children[0]
box2=self.children[1]
# and now you can decide which box you want to use add_widget method with
# in your case it should be
box2.add_widget(Button())

How to get kivy to show plot?

I'm working on a Kivy app which should generate plots when the user clicks on an item from a menu. When the user clicks the item, it opens a new screen which should generate the plot. My problem is that I can't seem to get Kivy to show the plot. My code executes without errors so I'm not sure where I'm going wrong.
I've looked for questions on this issue and the best I could find is here (Real-time plotting using matplotlib and kivy in Python) but that didn't work for me.
I also have a secondary question about re-defining the on_enter method of the Screen class so that it can use the App method for creating the plot. Is this possible?
Python code (function defined as part of the App class)
def grp_enter(self):
grid = GridLayout(rows = 1, cols = 1)
#grid.add_widget(FigureCanvasKivyAgg(plt.gcf())) should have prepared a canvas but one didn't appear
self.track_instance.associations() # generates the plot
KV code
<GroupScreen>:
on_enter: app.grp_enter()
name: 'groups_screen'
Button:
on_release:
app.root.transition = SlideTransition(direction = "right")
app.root.current = 'main'
text: 'back to the home screen'
font_size: 50
I could be wrong as I haven't been able to test your code but I believe that your screen doesn't get updated when you try to display your plot. You should try to use the schedule function:
clock.schedule_once(function_showing_plot).
So in your grp_enter function, you would call your track_instance.associations() function as such:
clock.schedule_once(track_instance.associations)
Hope this helps.

change kivy popup widget background in python

I have a kivy program that has a ‘sqlite’ database containing details of a golf competition which displays information & warning messages via a standard Pop-up function - pop_mess()
For testing purposes I open an alternative ‘testing’ database in a function – set_test_db(). When this is done I would like to change the background colour of the pop-up to warn the user that the main database is not being used.
I have tried to do this (code at end of 'set_test_db'). However although the routine retrieves the current background colour correctly it will not change it.
Can someone point me in the direction of a solution. The coding method works fine for changing the text properties of a widget. (similar questions usually refer to text properties). I have used both list & tuple for colour values.
In .ky file
<CustomPopup>:
popup_message: popup_message
size_hint: .98, .75
id: popup_id
title: "Casual and Ancient"
title_align: 'center'
title_color: 1,.5,.3,1
BoxLayout:
id: contentbox
orientation: 'vertical'
TextInput:
id: popup_message
color: .3,.4,1.0,1
background_color: [.7,1.0,.2,1]
text: "text message goes here"
font_size: 16
font_name: 'RobotoMono-Regular'
…..
CaaRoot:
<CaaRoot>:
orientation: 'lr-tb'
padding: 10
etc
...
in main.py
(self = CaaRoot)
...
def pop_mess(self,message):
p_up=CustomPopup()
lab=p_up.ids['popup_message']
lab.text=message
p_up.open()
...
def set_test_db(self):
# on button in CaaRoot
# open test data base instead of real
if self.db != None:
print 'Cannot Open Test Data Base - already running'
else:
# open ‘test’ database
# set background colour of popup to warn user.
fn='/home/.... caatestdb.db
self.op_db(fn)
self.testing='TEST database'
p_up=CustomPopup()
lab=p_up.ids['popup_message']
x=lab.background_color
new_col=[.9,.3,.3,1]
lab.background_color=new_col
self.pop_mess('color set to : '+str(new_col)+ ' was : '+str(x))
the popup displays
color set to : [0.9, 0.3, 0.3, 1] was : [0.7, 1.0, 0.2, 1]
(with background still set to [0.7, 1.0, 0.2, 1])
Have a look at kivymd, which solves issues like that very well. It is basically a very elegant template for kivy based on Google's Material Design.
For me it makes all the coloring stuff and appearance in general much easier, especially as I am not a designer but a programmer.
The Demo has also a very complete sets of examples including popups.
On further investigation of my problem I came across the similar question (Changing Background of Kivy Popup )
The fact that the solution given to that was “non-intuitive” involving the properties of the ‘inherited’ Canvas element(?) made me realise (remember ?) than some widgets do not have the full range of modifiable properties.
The 'background_color' property that I was retrieving was a property inherited from the Canvas which cannot be easily changed.
I have avoided the problem by creating a ‘popup’ widget whenever I wish to use it rather than modifying an existing widget. I still cannot (simply) change the background_color of the popup but I can change the background color of the parent element so that the user’s eye immeditely sees something different. I can also change the text color when calling the popup message function.
def pop_mess(self,message,colour=(.9,.6,.9,1)):
#
# text color can ve overridden by the 'colour' parameter
# self.backc can be set as required to set the color behind the
# popup depending on circumstances
popup=Popup(title='Casual & Ancient', \
content=Label(text=message,color=colour,\
font_name = 'RobotoMono-Regular'),\
size_hint=(.98,.75),background_color=self.backc)
popup.open()
I'm not completely sure that the analysis is correct or that there may be some side-effects but it seems to work ok (and was somewhat simpler to code)

Reading user-input data in Python 2.7 from Kivy file

Hopefully this will be an easy question for some of you. So I am working on some code to interface with a heater controller. The GUI is written mostly in Kivy language (to compartmentalize the various screens), but I have a .py file that ties all the Kivy files together. Within one my Kivy files, there are a few TextInput fields that are for a user to define a few numerical values. Once the text fields are filled, the user clicks on the "Start" button to begin the heater test.
Now, my question is when using the "Start" button as my flag, how can I get Python to read the numerical values that are in the fields? I've looked online and posts about using the .get and raw_input methods, but nothing that has Python retrieving and interpreting values from Kivy code. Any help would be much appreciated!
This is part pf my .py file, which where I need the user-input values to be evaluated...
class ControllerScreen(Screen):
def build(self):
return ControllerScreen()
def CreateExcelFile(self):
wb = Workbook()
ws = wb.active
ws.cell('A1').value = 'Timestamp'
ws.cell('B1').value = 'Temperature'
ws.cell('C1').value = 'Batch 1'
ws.cell(row=2, column=1).value = 000
wb.save("Spreadsheet1.xlsx")
This is an excerpt from the Kivy file, containing the code for a single text field that a user types a number into...
<ControllerScreen>:
TextInput: #Ramp time increment field
id: ramp_time
font_size: 25
size_hint: 0.1,0.06
pos_hint: {"right":0.575, 'y':0.67}
Button: #Start analysis button
on_release: app.root.current = "home"
text: "Start"
font_size: 25
color: 0,1,0.5,1
size_hint: 0.29,0.075
pos_hint: {'x':0.35, 'y':0.1}
And lastly, here is a picture of the GUI to give some reference to the text fields I am regarding (the one circled in red is the one I reference in the code above)...
To access value of TextInput you need to use TextInput's property text, therefore to get a number you need to:
n = int(self.ids.ramp_time.text) #or float or whatever you need
However if you won't access the text in your ControllerScreen, then you need to manage a communication between your classes, so that you could use variables between them. You can achieve it with these answers: 1, 2 for example.
There's an option for you to set a filtering for your input so your user won't screw something up(e.g. inputs letter/s)
I've looked online and posts about using the .get and raw_input methods, but nothing that has Python retrieving and interpreting values from Kivy code.
I think the problem is that you looked for getting user's input directly from python and not from Kivy. I'd say Kivy is quite new, so there will be no mention in old tutorials/websites. There are tutorials and docs directly for kivy either on official website or youtube.

In Kivy, why doesn't popup.dismiss() delete the popup from memory?

I'm currently struggling with a memory usage problem in Kivy.
When a popup is created and opened, memory usage goes up a little (which is normal), but when the user closes it (with the dismiss() method, which closes the popup and removes it from its parent), the memory isn't released.
So, if the user decides to open the popup a lot of times, the program will eventually use a lot of memory. Here is a part of my code that shows the problem by creating, opening and then dismissing a popup 500 times.
# py file
class OptionsView(Screen):
def popupLoop(self):
for x in range(0, 500):
popup = self.MyPopup()
popup.open()
popup.dismiss()
class MyPopup(Popup):
pass
# kv file
<OptionsView>:
BoxLayout:
orientation: "vertical"
Button:
text: "Popup Loop"
on_press: root.popupLoop()
<MyPopup>:
size_hint: (.6, .6)
title: "Confirmation"
BoxLayout:
Button:
text: "Cancel"
on_press: root.dismiss()
Pressing the "Popup Loop" button in the OptionView screen causes the program to jump from 1.2% memory usage to 11.7% (according to top). Resizing the window (which calls gc.collect()) does bring this number down a little bit, but it remains really high.
How can I prevent this from happening? (Bear in mind that I'm far from being a Python/Kivy expert, so even if the solution is really obvious to you, please try to explain it to me!)
popup.dismiss() will not remove the popup from memory immediately.
Maybe this will help How to force deletion of a python object?
Second; on why sometimes your popups get dismissed and sometimes they don't, you have to understand that UI frameworks need to be programmed using events. Events/progression do not happen in linear fashion.
Better test for what you are trying to check would be using Clock
self.pops = 0
Clock.schedule_once(self.test_pops)
def test_pops(self, dt):
if self.pops > 10:
return
self.pops += 1
pup = self.MyPopup()
pup.bind(on_parent=self.dismiss_pup)
pup.open()
def dismiss_pup(self, pup, parent)
# popup was opened, please close it now
pup.unbind(on_parent=self.dismiss_pup)
pup.dismiss()
Clock.schedule_once(self.test_pops)
It would be a lot simpler to just use the web debugger module geared towards this instead though.
http://kivy.org/docs/api-kivy.modules.webdebugger.html

Categories

Resources