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.
Related
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).
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'm sending a variable value from programa1 for a new object using :
def send_price(self):
self.pricesend = float(self.text1.get()) #this take a value from a tkinker.Entry
print(self.pricesend)
objetoprograma1.Object(self.pricesend)
the object "objetoprograma1" return a new value using:
class Object():
def __init__(self, price):
self.price_recibe = float(price)
print(self.price_recibe)
self.new_price = self.price_recibe + 10
print(self.new_price)
programa1.Aplication.recibe_newprice(self, float(self.new_price))
now I want to update the value in the principal1 tkinter.Entry called self.text1:
def recibe_newprice(self, new_price):
self.new_price = new_price
print("price new recibe" , self.new_price)
## this don't work.. this don't update or change the value in the tkinter.Entry
self.text1.delete(0, len(self.text1.get()))
self.text1.insert(self.my_main, str(self.new_price))
I have the following exception:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1482, in __call__
return self.func(*args)
File "B:\MAESTRIA\PYTHON\trabajos\hello\programa1.py", line 38, in send_price
objetoprograma1.Object(self.pricesend)
File "B:\MAESTRIA\PYTHON\trabajos\hello\objetoprograma1.py", line 19, in __init__
programa1.Aplication.recibe_newprice(self, float(self.new_price))
File "B:\MAESTRIA\PYTHON\trabajos\hello\programa1.py", line 51, in recibe_newprice
self.text1.delete(self.my_main, len(self.text1.get()))
AttributeError: 'Object' object has no attribute 'text1'
the full programa1.py
# -*- coding: latin-1 -*-
import tkinter
import objetoprograma1
import time
class Aplication():
def __init__(self,my_main):
self.my_main = my_main
self.variables()
self.GUI()
def variables (self):
self.price = None
self.list = []
def GUI(self):
self.text1 = tkinter.Entry()
self.text1.insert(0, "1000")
self.text1.grid(column = 0, row = 0)
self.boton1 = tkinter.Button(self.my_main, text = "sendprice", command = self.send_price )
self.boton1.grid(column=1, row = 0)
def send_price(self):
self.pricesend = float(self.text1.get())
print(self.pricesend)
objetoprograma1.Object(self.pricesend)
def recibe_newprice(self, new_price):
self.new_price = new_price
print("price new recibe" , self.new_price)
## this don't work
self.text1.delete(0, len(self.text1.get()))
self.text1.insert(self.my_main, str(self.new_price))
if __name__ == "__main__":
root = tkinter.Tk()
#root.geometry("800x500+0+0")
root.title("titulo")
app = Aplication(my_main=root)
root.mainloop()
and objetoprograma1.py
# -*- coding: latin-1 -*-
import programa1
import tkinter
import time
class Object():
def __init__(self, price):
self.price_recibe = float(price)
print(self.price_recibe)
self.new_price = self.price_recibe + 10
print(self.new_price)
programa1.Aplication.recibe_newprice(self, float(self.new_price))
Look at your Object class and look at the exception message. You are calling the recibe_newprice method, but passing it the Object instance (Object has no text1 attribute). The recibe_newprice is written for the Aplication class and as such expects self to be an instance of the Aplication class. You seem to be mixing up what classes are for or how the self argument works.
My first tip is to name things with more descriptive names. Names like Object, Application, and Program1 don't tell the reader anything about what the purpose of those objects are.
Second, do you know the difference between classes and functions? Maybe this will help. I would code the send_price method this way:
def send_price(self, price_recibe):
pricesend = float(self.text1.get())
print(pricesend)
print(price_recibe)
new_price = price_recibe + 10
print(new_price)
self.recibe_newprice(new_price)
If this doesn't make sense why I'm doing things this way or why this might be considered better/easier than the way you did it then I suggest researching how python classes, attribute assignment, and argument passing works.
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.
I'm using a not-well-known framework called IPKISS; hopefully, this does not matter.
from ipkiss.all import *
class ElectrodePair(Structure):
"""An electrode component to be used for a PPLN design."""
__name_prefix__ = "ELECTRODE_PAIR"
width = PositiveNumberProperty(required = True)
height = PositiveNumberProperty(required = True)
seperation = PositiveNumberProperty(required = True)
lay1 = Layer(number = 1, name = "boundaries")
def define_elements(self, elems):
left = ShapeRectangle(center = (-self.seperation*0.5,0.), box_size = (self.width, self.height))
right = ShapeRectangle(center = (self.seperation*0.5,0.), box_size = (self.width, self.height))
elems += Boundary(layer = self.lay1, shape = left)
elems += Boundary(layer = self.lay1, shape = right)
return elems
class ElectrodeStructure(ElectrodePair):
"""An array of electrodes."""
__name_prefix__ = "ELECTRODE_STRUCTURE"
amount = PositiveNumberProperty(required = True)
spacing = PositiveNumberProperty(required = True)
def define_elements(self, elems):
electrodePair = ElectrodePair.__init__(self)
elems += ARefX(reference = electrodePair, origin = (0,0), period_1d = self.spacing, n_o_periods_1d = self.amount)
return elems
def main():
FILE_NAME = "ElectrodeArray.gds"
electrodeArray = ElectrodeStructure(amount = 10, height = 100., seperation = 20, spacing = 10., width = 2.)
electrodeArray.write_gdsii(FILE_NAME)
if __name__ == "__main__":
main()
I have no idea why this is erroring. The error is:
File "/usr/local/lib/python2.7/dist-packages/IPKISS-2.4_ce-py2.7.egg/ipcore/properties/initializer.py",
line 327, in __init__ raise IpcoreAttributeException("Required property '%s' is not found in keyword arguments of '%s' initialization." % (p, str(type(self))))
ipcore.exceptions.exc.IpcoreAttributeException: Required property 'amount' is not found in keyword arguments of '<class '__main__.ElectrodeStructure'>' initialization.
It seems as though it's not happy with how I've passed my arguments, I've tried heaps of stuff and cannot get it to work. Advice would be much appreciated.
I suspect the error is due to electrodePair = ElectrodePair.__init__(self).
Thank you for your time.
You have to add __init__ method to your ElectrodeStructure class, that - as #hd1 has pointed out - has to set amount:
class ElectrodeStructure(ElectrodePair):
def __init__(self, amount):
ElectrodePair.__init__(self)
The way you call ElectrodePair.__init__ is wrong, since in the absence of ElectrodeStructure.__init__ in your class the former will be called automatically
EDIT:
Couple of things I've noticed on re-reading - you inherit from a class, and then within a class method you create an object of the parent class. Something is wrong here
class ElectrodeStructure(ElectrodePair):
[...]
def define_elements(self, elems):
electrodePair = ElectrodePair.__init__(self)
[...]
When you create a new ElectrodeStructure in main(), you're passing keword arguments. Becuase you're not defining an __init__ function in ElectrodeStructure, the super's __init__ is being called with those arguments (including amount, so there's no error).
Then in define_elements, you're calling __init__ again, except you aren't passing in any arguments, which is causing the error.
Additionally, this statement:
electrodePair = ElectrodePair.__init__(self)
Is assigning the return value (likely None) of ElectrodePair.__init__(self) to electrodePair. I suspect you want something more like the following:
electrodePair = ElectrodePair(amount=1, etc.)
It looks like you want composition, not inheritance; you could initialize your subclass in a proper __init__ function (as #volcano describes), or just create an ElectrodePair class member, and not inherit at all.