How to Update Label in kivy On Clicking a Button - python

I am Really Tired of Searching Related to This Topic. And never Getting Something Great Answer...

I am sorry to say that, but then you should question your search skills. Please provide next time a minimal example of what you already tried, because then I could help you with proper understanding the mechanisms. Of course, I could simply give you a solution, but this would not help you at all, because often there are different ways to get to the same result but in some situations, you should prefer one method to the other ones.
Back to your problem
Here is a simple example:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
import random
class MyApp(App):
def build(self):
self.layout = BoxLayout()
self.btn = Button(text="press", on_press=self.my_btn_method)
self.lbl = Label(text="")
self.layout.add_widget(self.btn)
self.layout.add_widget(self.lbl)
return self.layout
def my_btn_method(self, instance):
self.lbl.text = str(random.randint(0,100))
MyApp().run()
Explanation:
Every Button class and every class that inherits from the Button class has a on_press method.
You have basically two possibilities, either you write your own custom button class an overwrite the on_press method within this class or you write a custom method and assign this method to the buttons attribute on_press as I did in this example. When you do this, your custom method has to take the button instance as an attribute.
To change the labels text I simply assigned a new string to the labels text attribute. I really hope, this helps you with your problem. If you have some struggle just write a comment and I try to help you.
There is also an on_release method which you can use in the same way as the on_press method.

Related

How can I change the on_release functionality of a KivyMD button using Python code in KivyMD?

How can we change the on_release function of KivyMD button using Python code? I have added a print statement to the KivyMD button initially and I have changed on_release function of KivyMD button with Python code but the problem is, it joins my new function to the button instead of replacing its previous function. That mean, it also executes the previous function assigned to the button but I want to change function completely like replacing it with a new function using Python code.
Here is my Python code
self.root.ids.btn.on_release = self.new_function
Thanks in advance :)
If your initial on_release function is set in kv, then I don't think you can remove or replace it. I believe the fix is to extend the Button class and define the initial on_release function in its __init__() method. Like this:
class MyButt(Button):
def __init__(self, **kwargs):
self.on_release = partial(print, 'hi')
return super(MyButt, self).__init__(**kwargs)
Then replace that Button in your kv with MyButt, and remove the on_release: line fom the kv.

How to animate a button on kivy when the cursor is over it?

I'm kinda new to Kivy and I was looking for a way to animate the button when the cursor is over it.
I've tried to manage a way to get the mouse position and compare it with the button coordinates but no success at all.
This question has already been (mostly) answered at this post.There is a very nice example of this here by Olivier POYEN under LGPL license. Basically he has defined a class called HoverBehavior that you should inherit from to make a new class, e.g. HoverButton or HoverLabel (as his example shows). Then you have access to the on_enter and on_leave functions, which you can use to change the button's image, or change a label's text color, or whatever you need.
To answer your exact question, I would seek to understand the HoverBehavior class, then copy/paste it from the source above, then make a new class like so:
class HoverButton(Button, HoverBehavior):
def on_enter(self, *args):
self.background_normal = "some_image1.png" # Change the button's image when entered
def on_leave(self, *args):
self.background_normal = "some_other_image.png" # Change image when leaving
or you could use the kv language which looks even cleaner:
<HoverButton>:
background_normal: "some_image1.png" if self.hovered else "some_other_image.png"
just make sure you include a base class for the HoverButton in your python script if you use the 2nd option:
class HoverButton(Button, HoverBehavior):
pass

Tkinter button calling method on an object

I have some objects which are all instances of the same class; each object has (as one of its attributes) a tkinter button, and I want each tkinter button to perform a method on the object to which it belongs. I'm not really sure how to go about doing this. I've tried tinkering with lambda functions, and replacing "self" with "self_" in case tkinter was already passing "self" to the button's command, but none of this worked; I'm new to classes and hadn't come across lambda functions before today so it didn't surprise me. Example code is below - please could someone explain how to make it work in a way which is both simple, concise and pythonic, or if such a solution does not exist then provide a work around? Thanks in advance
import tkinter as tk
from tkinter import ttk
class SpecialButton():
def __init__(self, row):
self.button = ttk.Button(root, text="button", command=self.delete)
self.button.grid(row=row, column=1)
self.label = ttk.Label(root, text="label")
self.label.grid(row=row, column=2)
def delete(self):
self.button.forget()
self.label.forget()
#some other stuff
root = tk.Tk()
for row in range(3):
SpecialButton(row)
root.mainloop()
The only problem with your code is that you need to be calling grid_forget instead of forget.
Also, the code is a bit misleading -- the delete method doesn't actually delete anything, it just removes it from view. The widgets still exist and take up memory. Are you aware of that? If you truly want to delete the widgets, call the destroy method.

Beginner: Adding x number of widgets to layout

Really easy I'm sure but I'm learning Python & kivy (as a hobbyist not professional).
I have made my first 'complex' kivy layout, and am now attempting to add python code to it, and I am fundamentally mis-understanding some things. I am keeping it all organised in seperate files where possible.
1. Within a GridLayout I have a ScrollView. All I want is to be able to add 'x' number of buttons to the ScrollView in it's python class.
all relavent files ('...' indicating I have trimmed to only the relevant parts)
seatingmanager.py:
...
Builder.load_file('timescroll.kv')
...
class SeatingManager(AnchorLayout):
pass
class SeatingManagerApp(App):
def build(self):
return SeatingManager()
seatingmanager.kv:
<SeatingManager>
...
AnchorLayout:
...
GridLayout:
...
TimeScroll:
size_hint: None None
height: 50
width: 500
2. This is creating an instance of the TimeScroll class? This is where to add specific attributes to this instance?
timescroll.kv:
#:import timescroll timescroll
<TimeScroll>
3. This is where I can add attributes to all TimeScroll instances? If I am not adding any is this file necessary (other than importing timescroll.py)?
timescroll.py:(where I presume my problems lay)
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.scrollview import ScrollView
from kivy.uix.boxlayout import BoxLayout
class TimeScroll(ScrollView):
def build(self):
layout = BoxLayout(orientation='vertical', size_hint_x=None,width=1500)
for i in range(10):
btn = ToggleButton(text=str(i), group='timeHeaders')
layout.add_widget(btn)
self.add_widget(layout)
return layout
4. Is the build method automatically called when an instance of this class is created? If not, why is it called automatically in the first file?
5. This code doesn't work, simply leaves the ScrollView blank, so I presume I am adding the BoxLayout to the ScrollView incorrectly or the build method isn't automatically called.
Is the build method automatically called when an instance of this class is created? If not, why is it called automatically in the first file?
The build method is never called for widgets, unless you do so yourself. You should use the __init__ method, as per normal python convention (and don't forget to call super).
The App class has a build method that is called to start the user's own code, but the App is not a widget, and this is the only place kivy will automatically run a build method.

Tkinter - Entry .get() from another class

i've recently come across a problem thats bugging me with the tkinter entry .get() function, I have put together an example code so you can see what i'm trying to do, I have two classes, a class for each window. In the first window(main window) I have an entry box, in the second window I am attempting to get the entry box text from the first window.
Here's the code: (Trying to get entry box info from the first class in the second class)
from Tkinter import *
class window_1(object):
def __init__(self):
self.app = Tk()
self.app.title("Window One")
def entrybox(self):
self.ent = Entry(self.app) #This is the text i'm trying to get in 2nd class
def button(self):
def ODV(self):
class window_2(object):
def __init__(self):
self.app2 = Tk()
self.app2.title("Window Two")
def labels(self):
self.label_0 = Label(self.app2, text = "Name: ")
def info(self):
self.fetch_name = self.ent.get()#Here is my problem
def gridder(self):
self.label_0.grid(row = 0, column = 0)
self.fetch_name.grid(row = 0, column = 1)
rooter = window_2()
rooter.labels()
rooter.info()
rooter.gridder()
open_data_viewer = lambda: ODV(self)
self.but = Button(self.app, text = "Save", command = open_data_viewer)
def packer(self):
self.ent.pack(anchor = W)
self.but.pack(anchor = W)
def App_Runner(self):
self.app.mainloop()
root = window_1()
root.entrybox()
root.button()
root.packer()
root.App_Runner()
Your first problem is that you're creating more than one instance of Tk. You can't do that, tkinter isn't designed to work that way. If you want multiple windows, create instances of Toplevel. A tkinter program should always have exactly one instance of Tk, exactly one call to mainloop, and there should be little to no code following the call to mainloop.
Second, there is absolutely no value in embedding the definition of a class inside a function. Move it out, it will make your code easier to understand, and easier to write and maintain.
Third, for an instance of one object to access a method on another object, the first object needs to know about the second object or needs to know about a central "controller" object. This isn't a tkinter problem, it's a normal thing to consider when writing OO code.
From a practical standpoint, you need to pass in a reference either to the entry widget, or the object that contains the entry widget, when you create the second object. For example:
class window_2(object):
def __init__(self, other):
...
self.other = other
...
def info(self):
self.fetch_name = self.other.ent.get()
...
rooter = window_2(self) # pass "self" to the new object
This produces a tight coupling between the two objects -- the second object knows about the inner workings of the first object. This is not very good design, though for very, very simple programs it's not so bad. The problem is this: if you change the layout of the first widget, perhaps renaming "self.ent" to "self.some_other_frame.ent", you have to modify the other class too.
A better solution is to define in your first class a function that gets it's own value. Of course, ent serves that purpose, but again, that is a tight coupling. better to have a helper function:
class window_1(object):
...
def get_string(self):
return self.ent.get()
class window_2(object):
def info(self):
self.fetch_name = self.other.get_string()
This still has a loose coupling, but one that is much easier to manage because the coupling isn't tied to the specific internal layout and names of the first window. You can change the widgets all you want, as long as you continue to provide a get_string method that does what the other class expects. Your first class is providing a contract to the second class: a promise that no matter how else the window may change over time, it promises to provide this interface.

Categories

Resources