I'm learning python and Kivy and I'm really struggling to understand how to call functions and continue functions from a Kivy GUI.
Here is my .py:
import csv
import os
import easygui
import kivy
kivy.require('1.0.7')
from kivy.app import App
from kivy.animation import Animation
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.textinput import TextInput
def csvImport(filename):
with open(filename, 'rb') as f:
reader = csv.reader(f)
your_list = list(reader)
return your_list
class LoadFile(App):
def FileLoadScreen(self):
self.add_widget(Button(size_hint_y=(None), height=('48dp'), text='Select File',
on_press=self.ImportFile))
def ImportFile(self, instance):
filepath = easygui.fileopenbox()
if filepath!='.':
a=csvImport(filepath)
instance.text='File Loaded'
instance.disabled=True
class loginBAKApp(App):
def logAuth(username,password):
if username!='' and password!='':
print('ok')
kv_directory = 'GUI'
if __name__ == '__main__':
loginBAKApp().run()
And this is my loginBAK.kv:
#:kivy 1.9.0
GridLayout:
row_force_default: True
row_default_height: 40
rows: 3
cols: 2
padding: 10
spacing: 10
Label:
id: userLabel
text: 'Username:'
TextInput:
id: username
Label:
id: passwordLabel
text: 'Password:'
TextInput:
id: password
password: True
Button:
id:btn_login
text: 'Login'
on_press: print('OK')
This code seems to work without issues (when I click the login button, it does print 'OK'. I tried to swap it out with
on_press: logAuth(username,password)
and I get an error that logAuth is not defined.
Ultimately, what I'm trying to model here (as my first learning experience) is to hit the login button and as long as the fields are not blank, display a login success message for 5 seconds and then delete the fields and call the LoadFile app (add a button that can be clicked to select and import a file).
What exactly am I doing wrong here? I've sifted through about 60 scripts online and have been looking at the Kivy examples for hours and I cannot figure out how I'm doing this wrong. Can someone point me in the right direction and/or make suggestions as to creating/deleting the gui to do what I described? I'm new to Kivy (and can code basic python scripts) so this is all a little confusing to when I read some of the other questions on stackoverflow.
on_press: logAuth(username,password)
logAuth is a method of your app class, not a function defined in the kv namespace. You can instead use app.logAuth(...), app is a keyword that references the current App instance.
Related
I've been watching some tutorials on Kivy where a functional button prints a string in the console. However I haven't found a simple example where a functional button displays a string somewhere on the GUI (for instance a label) yet. So I tried to make one myself.
Looking for answers here I found some which are far too complex and cover several intertwined issues at the same time, which makes figuring out what would work in a simple example like this daunting, to say the least. Here's what I did so far:
simple.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
class SimpleWidget(BoxLayout):
result = ObjectProperty(None)
def button(self, result):
print(str(result.text))
class SupersimpleApp(App):
def build(self):
return SimpleWidget()
if __name__ == "__main__":
SupersimpleApp().run()
And here's supersimple.kv
<SimpleWidget>:
result: result
orientation: 'vertical'
BoxLayout:
id: box1
Label:
text: 'Type something'
TextInput:
id: result
height: 80
Button:
id: button
text: 'Press this'
on_press: root.button(result)
BoxLayout:
id: box2
Label:
id: your_input
What I want to achieve is to show ''result'' in your_input Label instead of printing it.
You can use the ids that you have defined in the kv, like this:
def button(self, result):
print(str(result.text))
self.ids.your_input.text = result.text
I'm editing a text of TextInput 'A' in Kivy application. I now need to copy text of TextInput 'B' to the A, by clicking on B, without A loosing it's focus.
Something like when I write an equation in Excel, I can click on another cell and the cell ID is copied to the equation, instead of selecting the another cell.
How would I do this, please?
Thanks.
Not really sure if that is what you are looking for. If you click into the second TextInput it will copy the content of the first TextInput. I am using a main.py
# main.py
from kivy.app import App
from kivy.properties import StringProperty
class AnswerApp(App):
text_of_text_input_1 = StringProperty()
def change_text_of_text_input_2(self):
self.text_of_text_input_1 = self.root.ids.text_input_1.text
if __name__ == "__main__":
AnswerApp().run()
and the kv file answer.kv.
# answer.kv
BoxLayout:
orientation: "vertical"
TextInput:
id: text_input_1
text: "text_input_1"
TextInput:
text: app.text_of_text_input_1
on_focus: app.change_text_of_text_input_2()
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
I'm a complete novice to both python and kivy having learnt python from codeacademy about a week ago and kivy from youtube tutorials.
Could someone please explain to me why the code below does not result in a screen with a label displaying n, which is incremented by the button?
Python file
import kivy
from kivy.app import App
from kivy.uix.button import Button, Label
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import NumericProperty
class Example(BoxLayout):
n = 0
def n_plus(self):
self.n += 1
class ExampleApp(App):
def build(self):
return Example()
example = ExampleApp()
example.run()
KV file
<Example>:
BoxLayout:
Label:
text: str(root.n)
Button:
text: "+1"
on_press: root.n_plus()
Then could you explain why making n = NumericProperty(0) makes this work?
I'd like to run some functions on n which don't seem to work on numeric properties.
Because when you use NumericProperty()
As the official document said:
It produces events such that when an attribute of your object changes,
all properties that reference that attribute are automatically
updated.
So, in short, it creates a binding relationship between your UI(.kv) and attribute of its class(.py)
But, actually, you can modify the UI by yourself without the help from the kivy framework. I changed your example as the following:
Add an id attribute to your widget
Access the id attribute by using self.ids.your_id_in_kv_file
But it's obviously not good, since now you need to update your UIby yourself everytime you want to update your UI. But with that XXXXProperty from kivy, you just need to change the value of that attribute, you don't need to worry about the UI at all.
Another disadvantage of the solution is that, when you need to change the UI, you need to change tons of code if you modify them all by yourself...
Here is the example:
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.lang.builder import Builder
from kivy.uix.boxlayout import BoxLayout
Builder.load_string("""
<Example>:
BoxLayout:
Label:
id: lbl
text: "0"
Button:
text: "+1"
on_press: root.n_plus()
""")
class Example(BoxLayout):
def n_plus(self):
value = self.ids.lbl.text
self.ids.lbl.text = str(int(value) + 1)
class ExampleApp(App):
def build(self):
return Example()
if __name__ == '__main__':
ExampleApp().run()
My app works perfectly for below code. ie open google.com once button is clicked.
#!/usr/bin/kivy
import kivy
kivy.require('1.7.2')
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
import webbrowser
Builder.load_string('''
<MenuScreen>:
GridLayout:
padding: 5
spacing: 5
cols: 1
padding: root.width*0.1
Button:
background_normal: ''
background_color:(0.862, 0.078, 0.235, 0.9)
text: 'PLAY'
font_size: '20sp'
on_press: root.val1()
''')
class MenuScreen(Screen):
def val1(self):
print "i am executed"
webbrowser.open("http://google.com/")
sm = ScreenManager()
menu = MenuScreen(name='menu')
sm.add_widget(menu)
class MainApp(App):
def build(self):
return sm
if __name__ == '__main__':
MainApp().run()
Now, suppose someone is using my app and after few days i want to change url from google.com to yahoo.com, what approach should i follow. I am completely clueless about it. Should i get a domain name and change it redirection like abc.com/test.html to redirect to google.com and then change the redirection to yahoo.com later or some other approach?
I can give you two idea for this:
keep a array of urls in your code and choose one randomly to use.
If you maintain a server, then you can use an static url from where your program will receive data which are nothing but url or url list and use it.