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).
Related
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)
I'm new to python and I am trying to do something like this: I have two classes, one for building a GUI form, and another class to handle the request. the submit button is on the GUI form, and the handle action is in the handle class.
So I tried to call the handle class from the GUI class, so I did something like this:
class Appview:
def __init__(self, master):
master.title("Simple EXCEL")
master.resizable(False, False)
self.filename = ""
self.input_file = ttk.Entry(self.frame_content, textvariable = self.filename, width = 24)
self.input_file.grid(row = 2, column = 1, pady = 5, padx = 5)
ttk.Button(self.frame_content, text = "choose file", command = self.browse_file).grid(row = 2, column = 2)
#ADDING#
a = HandleData(self)
ttk.Button(self.frame_content, text = "add", command = HandleData.submit).grid(row = 3, column = 0 ,columnspan = 3, pady = 5)
"""
Choosing a file to browse
"""
def browse_file(self):
self.filename = filedialog.askopenfilename(title = 'Choose a ffffile')
self.input_file.insert(0, self.filename) #updating the file entry
and this is the Handle class:
class HandleData:
def __init__(self):
self.the_file = Appview.filename.get()
def submit(self):
messagebox.showinfo(title = "Okie Dokie", message = "well done: {}".format(self.the_file))
but I keep getting this error:
Traceback (most recent call last):
File "C:\Users\Amir\workspace\Python Workout\main.py", line 91, in if __ name__ == "__ main__": main()
File "C:\Users\Amir\workspace\Python Workout\main.py", line 88, in main
appview = Appview(root)
File "C:\Users\Amir\workspace\Python Workout\main.py", line 60, in __ init__
a = HandleData(self)
TypeError: __ init__() takes 1 positional argument but 2 were given
any ideas please?
When you do -
a = HandleData(self)
You are trying to create the instance (object) of HandleData , when creating an instance, the __init__() is called (after the object has been created) with the first argument as the created instance itself, so when you send in self , it becomes the second argument for __init__() , but your __init__() in HandleData only accepts one argument , so the issue occurs.
You can make HandleData accept a second parameter which could be the AppView and then in the HandleData.submit , you can get the filename from this object. Example -
HandleData -
class HandleData:
def __init__(self, appview):
self.appview = appview
def submit(self):
filename = self.appview.filename
messagebox.showinfo(title = "Okie Dokie", message = "well done: {}".format(filename))
This gets the data for filename from the instance variable filename in AppView , if you want the data from the Entry input_file , you can use -
filename = appview.input_file.get()
Why the changes to submit() and HandleData are required is because we cannot get the filename when we do __init__() itself, because you create the object for HandleData in AppView.__init__() , so you have not selected any filename till then. So instead you are saving the appview object in self.appviewand when submit button is clicked, you are accessing the filename attribute set to it and doing your logic.
Change in AppView class -
self.datahandler = HandleData(self)
ttk.Button(self.frame_content, text = "add", command = self.datahandler.submit).grid(row = 3, column = 0 ,columnspan = 3, pady = 5)
a = HandleData(self)
Look at this, what are you passing though self? A reference of the class, you are in. But HandleData needs the reference of the class which it belong, and it automatically get it.
So, it basically gets 2 parameter, where 1 should be got.
Try:
a = HandleData()
You need and extra parameter in your __init__ function to hold the second class:
def __init__(self, other_class):
self.the_file = other_class.filename.get()
And then you can call it like you do:
a = HandleData(self)
Because in this case self refers to the AppView class. When in the HandleData class' __init__ function it refers to the HandleData class.
Look at the docs here about classes.
I have a marvellous function here:
def update_config(val):
config = configparser.ConfigParser()
fonts_comb = ""
for i in range(len(fonts)):
if i == len(fonts) - 1:
fonts_comb = fonts_comb + fonts[i]
else:
fonts_comb = fonts_comb + fonts[i] + ", "
config["LISTS"] = {"Fonts": fonts_comb}
config["variables"] = {"font_size": (screen_font_size.var).get(),
"x_offset": (x_offset_spin.var).get(),
"y_offset": (y_offset_spin.var).get(),
"language": language,
"preview_font_size": (preview_font_size_spin.var).get()}
variables = config["variables"]
if (fonts_menu.var).get() != strings[17]:
variables["font"] = (fonts_menu.var).get()
else:
variables["font"] = font
if (preview_fonts.var).get() != strings[18]:
variables["preview_font"] = (preview_fonts.var).get()
else:
variables["preview_font"] = preview_font
with open("config.ini", "w") as configfile:
config.write(configfile)
I don't know if it's relevant, too, but basically it does what the name says - updates the config file.
What I don't like about the function is that it requires an argument (val here, should be self maybe?). And 'cause it requires that argument, I can't call it "properly". Let me demonstrate, the following works just fine:
class OptionMenus(tk.OptionMenu):
def __init__(self, master, status, *fonts):
self.var = tk.StringVar(master)
(self.var).set(status)
(tk.OptionMenu).__init__(self, master, self.var, *fonts,
command = update_config)
However - calling like the following returns this error: TypeError: update_config() takes 0 positional arguments but 1 was given
class Spinboxes(tk.Spinbox):
def __init__(self, master):
self.var = tk.IntVar()
tk.Spinbox.__init__(self, master, textvariable = self.var,
from_ = 0, to = 100, command = update_config)
For now, I have solved it using this:
def crossover():
val = ""
update_config(val)
But it seems to be kind of a monkey-ish way to do things, so is there a better way to call that function?
Use a default argument value:
def update_config(val=None):
# etc.
You could also remove the argument entirely and use a single-argument lambda to call it in a context where that the argument must be passed:
def update_config():
# etc.
# ...
tk.OptionMenu.__init__(self, master, self.var, *fonts,
command=lambda _: update_config())
But I think the first option is simpler.
update_config looks like an instance method, so yes, I recommend using the accepted variable self.
If an error says it takes 0 arguments but 1 was given, that means exactly what it says. This means that calling update_config from the Spinbox object passes it an argument. However, since it works fine from OptionMenus and therefore works without an argument, you need to set it up to handle an optional argument.
Change:
def update_config(val):
to:
def update_config(self, event=None):
and that should fix the issue.
I have created a list of entries in a for-loop. All entries are stored in a list so I can just obtain all of the inputs later:
inputs = [e.get() for e in self.entries]
However, I have also created a button next to each entry in the for-loop (so they each call the same function). How can I make it so that it recognizes which button belongs to which row/entry? Is there something I can do with event?
row = 0
self.entries = []
self.comments = []
for n in names:
e = Entry(self.top, bd = 5)
e.insert(0, n)
e.grid(column = 1, row = self.row, sticky = 'NSWE', padx = 5, pady = 5)
self.entries.append(e)
self.comments += [""]
commentButton = Button(self.top, text = "comment", command = self.commentSelected)
commentButton.grid(column = 3, row = self.row, sticky = 'NSWE', padx = 5, pady = 5)
self.row = self.row + 1
Yes -- use Callback Shims ( Currying Functions )
( courtesy Russell Owen )
I find I often wish to pass extra data to a callback function, in addition that that normally given. For instance the Button widget sends no arguments to its command callback, but I may want to use one callback function to handle multiple buttons, in which case I need to know which button was pressed.
The way to handle this is to define the callback function just before you pass it to the widget and include any extra information that you require. Unfortunately, like most languages, Python doesn't handle the mixing of early binding (information known when the function is defined) and late binding (informtation known when the function is called) particularly well. I personally find the easiest and cleanest solution is:
Write my callback function to take all desired data as arguments.
Use a callback shim class to create a callable object that stores my function and the extra arguments and does the right thing when called. In other words, it calls my function with the saved data plus the data that the caller supplies.
I hope the example given below makes this clearer.
The callback shim I use is RO.Alg.GenericCallback, which is available in my RO package. A simplified version that does not handle keyword arguments is given in the example below. All shim code is based on a python recipe by Scott David Daniels, who calls this "currying a function" (a term that is probably more common than "callback shim").
#!/usr/local/bin/Python
""" Example showing use of a callback shim"""
import Tkinter
def doButton(buttonName):
""" My desired callback.
I'll need a callback shim
because Button command callbacks receive no arguments.
"""
print buttonName, "pressed"
class SimpleCallback:
""" Create a callback shim.
Based on code by Scott David Daniels
(which also handles keyword arguments).
"""
def __init__(self, callback, *firstArgs):
self.__callback = callback
self.__firstArgs = firstArgs
def __call__(self, *args):
return self.__callback (*(self.__firstArgs + args))
root = Tkinter.Tk()
buttonNames = ( "Button 1", "Button 2", "Button 3" )
for name in buttonNames:
callback = SimpleCallback( doButton, name )
Tkinter.Button( root, text = name, command = callback ).pack()
root.mainloop()
You can also use lambda:
from tkinter import *
def bla(b):
...
root = Tk()
buttons = []
for i in range(...):
button = Button(root)
button.configure(command=lambda b=button: bla(b)) # Make sure the Button object already exists
buttons.append(button)
button.pack()
root.mainloop()
As far as I see, you can't create the buttons in a single list comprehension now, but it is simpler and more readable than a class imho.
Edit
So I asked this question earlier and I received some good insight, but I feel like my question wasn't really answered. I'm building a small program to practice with python and making GUI's and I'm having a small problem with a button command.
#This is the temperature menu:
def temperM(self, *args):
self.clearscreen(self.frame)
self.frame2 = Frame(self.root)
self.frame2.grid(column = 0, row = 0)
self.firstunit = StringVar()
self.secondunit = StringVar()
self.entryspace = IntVar()
self.displayspace = IntVar()
#Create back button
#This is the part that needs to be fixed
self.back = Button(self.frame2, text = "< Back",
command = lambda: self.redo(self.frame2))
self.back.grid(column = 1, row = 3)
self.label = Label(self.frame2, text = "Convert from: ")
self.label.grid(column = 1, row = 1, padx = 4)
#Create the check boxes
self.celsius = Checkbutton(self.frame2, text = "Celsius",
variable = self.firstunit, onvalue = 'celsius')
self.celsius.grid(column = 2, row = 1)
self.fahrenheit = Checkbutton(self.frame2, text = "Fahrenheit",
variable = self.secondunit, onvalue = 'fahrenheit')
self.fahrenheit.grid(column = 3, row = 2)
#Create entry space to recieve text
#This is where the problem starts.
self.entry = Entry(self.frame2, width = 7,
textvariable = self.entryspace)
self.entry.grid(column = 3, row = 3)
self.compute = Calculate(self.entryspace.get())
self.button = Button(self.frame2, text = "Calculate",
command = lambda: self.displayspace.set(self.compute.celtoFah()))
self.button.grid(column = 3, row = 4)
self.display = Label(self.frame2, textvariable = self.displayspace)
self.display.grid(column = 2, row = 2)
I have this function inside of a class Menu with def__init__(self, root) which creates all the different menu options.
class Calculate:
def __init__(self, number):
self.number = number
def celtoFah(self):
try:
self.temp = Temperature()
self.number = float(self.number)
return self.temp.C2F(self.number)
except ValueError:
pass
And I have this class which holds all the different calculations that will be used in the code.
What I'm having trouble with is with my button command command = lambda: self.displayspace.set(self.compute.celtoFah()). When I run the code and press 'Calculate' which runs the command, self.displayspace.set(), it doesn't set self.displayspace to what I believe the returned value should be. Instead it returns and sets self.displayspace to what self.entryspace.get() is originally without modifications which is 32 and 0 respectively, which causes me to believe that the line self.compute = Calulate(self.entryspace.get()) is not updating when I put in a new value so self.entryspace is not getting a new value but its retaining the same initial value established by IntVar(). Am I doing something wrong in my code for self.entryspace not to be updating with a new value? At first I had it as a StringVar() which would convert to a float in celtoFah but I was throwing ValueError because it was receiving an empty string even after a user inputs a value. I really want to keep all calculations in a separate class since I will be having 20+ in the final version, but should I move these commands into class Menu or is there another I can do this by having a separate class? If you need to see my full code here is a link to it on github: https://github.com/Flameancer/Unit-Conversion-Program-in-Python
In general, you don't pass values between classes, but between instances of those classes. At any given time, you may have 0, 1, or 30 different Foo instances; how does a Bar instance even know which one you want?
The first question is, who's calling that something method on that Bar? Whoever it is, he has the value. Maybe it should be the Foo instance that's doing the calling.
For that to happen, the foo instance has to know about a bar instance. Maybe you want to create one in the constructor:
class Foo:
def __init__(self, argument):
# ...
self.bar = Bar(42)
# ...
… and now you can use it the same way as any other member:
def function(self, *args):
# ...
randomness = self.bar.something()
self.displayfield.set(randomness)
# ...
Or maybe you're already constructing one somewhere, and you just want to pass it to the Foo instance as a constructor:
class Foo:
def __init__(self, argument, bar):
# ...
self.bar = bar
# ...
bar = Bar(42)
foo = Foo(23, bar)
Or maybe you want to construct a new one locally each time you call the function method. Or maybe you want a global Bar instance shared by everyone, no matter how many Foo instances you have. Or…
As you can see, there are many different possible relationships between a Foo instance and a Bar instance, and which one is appropriate depends entirely on what Foo and Bar actually represent. This is the code idea behind object modeling: there are things of some kind that your objects represent, and the relationships between those things are reflected in the relationships between those objects.