Kivy how to access widget in function, no KV just python code - python

Sorry for my very basic problem but I have been searching around of an answer for an hour to no avail.
this is my basic code:
class LoginScreen(Screen):
def __init__(self,**kwargs):
super(LoginScreen,self).__init__(**kwargs)
l = BoxLayout(orientation='vertical')
sound_btn = Button(text="Play Sound", font_size = 300)
word_bx = TextInput(multiline=False,hint_text='type here...',font_size=300)
submit_btn = Button(text = 'Submit', font_size=300)
submit_btn.bind(on_press = self.submitAction)
l.add_widget(sound_btn)
l.add_widget(word_bx)
l.add_widget(submit_btn)
self.add_widget(l)
def submitAction(self,*args):
if self.word_bx.text == KivyApp.word:
KivyApp.point+=1
self.word_bx.text=""
KivyApp.i +=1
print(KivyApp.point)
As you can see, in the submitAction function I want to reference the word_bx widget, but this throws an error stating that Class LoginPage has no attribute word_bx. I then tried to place 'ids' infront of self but that throws another error. What am I doing wrong? sorry for my incompetence..

These lines
word_bx = TextInput(multiline=False,hint_text='type here...',font_size=300)
...
l.add_widget(word_bx)
define word_bx as a temporary variable. It's not available after __init__() exits. If you want it to be persistent so that you can access it from other methods, you need to make it a member of the class.
self.word_bx = TextInput(multiline=False,hint_text='type here...',font_size=300)
...
l.add_widget(self.word_bx)
Then the line
self.word_bx.text=""
in submitAction() will refer to something that actually exists.

I fixed it using Lambda and making the function external to the class:
def submitAction(text):
if text == KivyApp.word:
KivyApp.point+=1
KivyApp.i +=1
print(KivyApp.point)
class LoginScreen(Screen):
def __init__(self,**kwargs):
super(LoginScreen,self).__init__(**kwargs)
l = BoxLayout(orientation='vertical')
sound_btn = Button(text="Play Sound", font_size = 300)
word_bx = TextInput(multiline=False,hint_text='type here...',font_size=300)
submit_btn = Button(text = 'Submit', font_size=300)
submit_btn.bind(on_press = lambda *a:submitAction(word_bx.text))
l.add_widget(sound_btn)
l.add_widget(word_bx)
l.add_widget(submit_btn)
self.add_widget(l)

Related

how to clear text from a text widget from a class method?

I am new to OOPs. I wrote following code where delText method clears text field when clicked on a text widget. I called delText method by binding it with a <FocusIn> but I am getting error
AttributeError: 'GuiAndFileMethods' object has no attribute 'delete'
and further I want to read text in some other method. I know that my method is not recognizing the widget on which delete to be done. so how to do it ?
my code
from tkinter import *
class GuiAndFileMethods(Frame):
def delText(obj,event=None):
obj.delete("1.0", END)
z = GuiAndFileMethods()
root = Tk()
fileName = Text(root, height = 1, width = 57, wrap = None )
fileName.insert(INSERT, "Filename")
fileName.grid(row = 1, column = 0,columnspan = 5, padx = (10,50),sticky = W)
fileName.bind("<FocusIn>", lambda x: z.delText(fileName))
replacementNum = Text(root, height = 1, width = 18, wrap = None )
replacementNum.insert(INSERT, "No Of Replacements")
replacementNum.grid(row = 1, column = 6,columnspan = 1,sticky = E)
replacementNum.bind("<FocusIn>", lambda x: z.delText(replacementNum))
root.mainloop()
You have to either define your function as a static function:
class GuiAndFileMethods(Frame):
#staticmethod
def delText(obj,event=None):
obj.delete("1.0", END)
Or pass self as the first argument of the function:
class GuiAndFileMethods(Frame):
def delText(self, obj,event=None):
obj.delete("1.0", END)
In this case, since you are not using any attribute of the class that you are defining, I suggest to go with the first approach.
When you define a method like that, Python will automatically insert the instance of the class as the first argument. This is called self by convention, but it doesn't have to be. So obj there is actually the instance of GuiAndFileMethods class, NOT the object you passed in. The object you pass in will be the second argument of:
def delText(instanceof_GuiAndFileMethods, obj, event=None):
obj.delete("1.0", END)
So, how you define it and how you call it are slightly different. You would call it like this:
instanceof_GuiAndFileMethods.delText(obj, event)
That object you call the method on gets inserted as the first argument (again, usually self).

Python - Functions & Execution order

I want to use the bot_create function with a button but I keep getting (on line 20) the problem "bots not defined" so I moved the function down below the button but got the problem "bot_create not defined".
I didn't get this problem using C++ and I'm new to Python. How should I arrange the functions?
import tkinter as tk
import numpy as np
import multiprocessing as mp
bots_max = 1000 # Maximum number of bots
bot = []
bot_count = 0
# Menu functions
def save_field():
pass
# Field functions
def field_clear():
pass
# Bots functions
def bots_create():
bot[bot_count] = bots
bot_count += 1
main = tk.Tk()
field_sides = 600
ctrls_width = 200
main.geometry("800x600")
main.resizable(0, 0)
main.title("Swarm Simulator v1.0")
# Controls menu on left side
button1 = tk.Button(main, text = "Button 1").pack(side = "left", command = bots_create())
class environment:
def __init__():
pass
class wall:
def __init__():
pass
# Bots
class bots:
alive = True
def __init__():
alive = True
# Field where bots live
field = tk.Canvas(main, width = field_sides, height = field_sides, bg = "white").pack(side = "right")
for particle in bots:
print("|")
main.mainloop()
Here's a version of your code that fixes all the syntactic problems, and so compiles (what I really mean is that my IDE now thinks its ok). It also runs, but I don't know if it does what you intended. See my comments in the code:
import tkinter as tk
import numpy as np
import multiprocessing as mp
# moved your class defs up to fix problems with accessing them before they are defined
class environment:
def __init__(self): # need a self param here
pass
class wall:
def __init__(self): # need a self param here
pass
# Bots
class bots:
alive = True
def __init__(self): # need a self param here
alive = True
bots_max = 1000 # Maximum number of bots
bot = []
# bot_count = 0 # this no longer does anything. use `len(bot)` to get the number of objects in the 'bot' list
# Menu functions
def save_field():
pass
# Field functions
def field_clear():
pass
# Bots functions
def bots_create():
# bot[bot_count] = bots # this will crash as it is referring to a non-existent location in the list
# also, your use of "bots" here makes no sense
# bot_count += 1 # this makes 'bot_count' a local variable, which is not what you want
bot.append(bots()) # not sure this is what you want, but this creates a new 'bots' object and adds it to the 'bot' list
main = tk.Tk()
field_sides = 600
ctrls_width = 200
main.geometry("800x600")
main.resizable(0, 0)
main.title("Swarm Simulator v1.0")
# Controls menu on left side
button1 = tk.Button(main, text = "Button 1").pack(side = "left", command = bots_create())
# Field where bots live
field = tk.Canvas(main, width = field_sides, height = field_sides, bg = "white").pack(side = "right")
for particle in bot: # maybe you want to iterate over the 'bot' list instead of the 'bots' type?
print("|")
main.mainloop()
As #khelwood says, it seems that you should swap the use of the names bot and bots per the way you are using them

Updateing a Kivy ListProperty item works in one function but throws TypeError in another

I have been working on a Python app with Kivy for several weeks(my first real dive into Python) and have hit a snag with working with a ListProperty() item linked to kv Label text.
The Lists are being created and are populating the Label text using root.var[indx] in the .kv file. My issue comes in updating a specific index point in the ListProperty. I have one function that it works every time. Then another that always errors with:
TypeError: 'kivy.properties.ListProperty' object does not support item assignment
Part of my code:
from kivy.properties import StringProperty, ObjectProperty, ListProperty, NumericProperty
import... #continued
class setScreen(screen):
#other variables here
trig0 = ['tr0', 'Pre-Mass', 'yuo', '4', 1, colNorm, 'trig0p']
...
trigIndex = {}
trigList = [trig0, ...]
trigDict = {'trig0p':trig0p, ...}
trig0p = ListProperty(trig0[:])
...
def __init__(self, **kwargs):
#do init here
def custController(self):
#this screen's main functions
#The following function works and the Kivy Label text is updated without issue
def changeProcess(self, x): #x is list from custController button eg.['trig0p', 'Text', 'New Text', 'more new text', #, 'something']
itemToChange = x[0]
self.trigDict[itemToChange][2] = x[2]
self.trigDict[itemToChange][3] = x[3]
#The following function is for a remote update from a different class
#This fails with a 'TypeError:'
def progUpdate(self, y): #y again is a list
itemToChange = y[0]
self.trigDict[itemToChange][2] = y[2]
self.trigDict[itemToChange][3] = y[3]
class ...other screens and manager
#The following Popup is called from a button in the main functions
class MassSetPopup(Popup):
settingID = ''
settingPart = StringProperty()
settingLoc = StringProperty()
settingNum = StringProperty()
full_num = ''
new_lineData = []
def __init__(self, **kwargs):
super(MassSetPopup, self).__init__(**kwargs)
def on_parent(self, *args):
self.settingPart = self.new_lineData[1]
self.settingLoc = self.new_lineData[2]
self.settingNum = self.new_lineData[3]
def num_clk(self, x):
if x == 'Bksp':
self.full_num = self.full_num[:-1]
self.settingNum = self.full_num
self.new_lineData[3] = self.full_num
elif len(x) > 3:
self.settingLoc = x
self.new_lineData[2] = x
elif len(self.full_num) == 4:
pass
else:
self.full_num = self.full_num+x
self.settingNum = self.full_num
self.new_lineData[3] = self.full_num
def save_point(self):
setScreen.progUpdate(setScreen, self.new_lineData)
self.dismiss()
def cancel_point(self):
print(self.new_lineData)
self.dismiss()
I have placed the returning data in a non ListProperty list in the setScreen class as a test and it is updating properly from the Popup class.
All the .kv widgets load properly and screens/popups do what is expected.
I have been struggling with this for a couple days and have searched high an low but could not find anything that fit this issue, or even pointed me in the right direction, at least to my novice Python coding.

button command option in tkinter

In the little GUI app below. When I use button's command option to call a function. It doesn't work like this: self.update() rather it works like this: self.update. Why so? Is is some special way that command option of a button works? I think a method or a function should be called with those braces (), unless it's a property:
i.e.
#name.setter:
def setter(self, name):
self.name = name
#main
object.name = "New_obj"
Note: The above is just a template so you might get my point. I didn't write the complete valid code. Including class and everything.
from tkinter import *
class MuchMore(Frame):
def __init__(self, master):
super(MuchMore,self).__init__(master)
self.count =0
self.grid()
self.widgets()
def widgets(self):
self.bttn1 = Button(self, text = "OK")
self.bttn1.configure(text = "Total clicks: 0")
self.bttn1["command"] = self.update # This is what I am taking about
self.bttn1.grid()
def update(self):
self.count += 1
self.bttn1["text"] = "Total clicks" + str(self.count)
#main
root = Tk()
root.title("Much More")
root.geometry("324x454")
app = MuchMore(root)
It is a high order function, meaning you are referencing a function as an object. You are not calling the function and assigning the command to the return value of the function. See here for more information.
The command parameter takes a reference to a function -- ie: the name of the function. If you add parenthesis, you're asking python to execute the function and give the result of the function to the command parameter.

classmethod for Tkinter-Monitor-Window

I would like to realise a monitor window that reports the user about ongoing computations. To do so I wrote a little class. But as I would like to use it accross different modules in an easy fashion I thought to implement it with classmethods. This allows to use it in the following way without instances:
from MonitorModule import Monitor
Monitor.write("xyz")
Also, if I use it in an other module, the output of Monitor.write() within other_module.py will be displayed in the same window.
This I can import in each module to redirect specific outputs to the same monitor. I got it to work except one little thing that I don't understand. I can't close the Monitor-window with the specific handler that I wrote. I could do it with the non-classmethod-way but not with the handler as a classmethod.
Look at the code:
import Tkinter
class Monitor_non_classmothod_way(object):
def __init__(self):
self.mw = Tkinter.Tk()
self.mw.title("Messages by NeuronSimulation")
self.text = Tkinter.Text(self.mw, width = 80, height = 30)
self.text.pack()
self.mw.protocol(name="WM_DELETE_WINDOW", func=self.handler)
self.is_mw = True
def write(self, s):
if self.is_mw:
self.text.insert(Tkinter.END, str(s) + "\n")
else:
print str(s)
def handler(self):
self.is_mw = False
self.mw.quit()
self.mw.destroy()
class Monitor(object):
#classmethod
def write(cls, s):
if cls.is_mw:
cls.text.insert(Tkinter.END, str(s) + "\n")
else:
print str(s)
#classmethod
def handler(cls):
cls.is_mw = False
cls.mw.quit()
cls.mw.destroy()
mw = Tkinter.Tk()
mw.title("Messages by NeuronSimulation")
text = Tkinter.Text(mw, width = 80, height = 30)
text.pack()
mw.protocol(name="WM_DELETE_WINDOW", func=handler)
close = handler
is_mw = True
a = Monitor_non_classmothod_way()
a.write("Hello Monitor one!")
# click the close button: it works
b = Monitor()
Monitor.write("Hello Monitor two!")
# click the close button: it DOESN'T work, BUT:
# >>> Monitor.close()
# works...
So, the classmethod seems to work and also seems to be accessible in the right way! Any idea, what went wrong, that it doesn't work with the button?
Cheers, Philipp
You don't need lots of classmethods just to make it easy to use an object across multiple modules.
Instead consider making an instance at module import time as shown here:
import Tkinter
class Monitor(object):
def __init__(self):
self.mw = Tkinter.Tk()
self.mw.title("Messages by NeuronSimulation")
self.text = Tkinter.Text(self.mw, width = 80, height = 30)
self.text.pack()
self.mw.protocol(name="WM_DELETE_WINDOW", func=self.handler)
self.is_mw = True
def write(self, s):
if self.is_mw:
self.text.insert(Tkinter.END, str(s) + "\n")
else:
print str(s)
def handler(self):
self.is_mw = False
self.mw.quit()
self.mw.destroy()
monitor = Monitor()
other_module.py
from monitor import monitor
monitor.write("Foo")

Categories

Resources