Referring to dynamically determined class variables (self.[name]) - python

new to python and have been googling around. Am trying out python class with a simple calculator.
Am currently using tkinter.
self.grid()
self.customfont = font.Font(family="Arial", size=14)
self.entryVariable=tkinter.StringVar()
dictFields = {"S0":"Enter initial price, S0, here.",
"K":"Enter strike price, K, here.",
"T":"Enter time to maturity, T, here.",
"r":"Enter risk-free rate, r, here.",
"sigma":"Enter volatility, sigma, here.",
"q":"Enter dividend yield, q, here."
}
i = 2
for key in dictFields.keys():
self.entryVariable = tkinter.StringVar()
## Unsure of this but I googled that I can create dynamically named class variables on the fly in this manner - would like to check if this is correct and if it is the best way to do it?
setattr(self, key, 0)
## another difficulty is that subsequently i have to set the properties of this variables - self.key seems to return be the literal self.key rather than self.S0, self.K etc.
self.key = tkinter.Entry(self, textvariable=self.entryVariable, justify="center")
self.key.grid(row=i, column=1, sticky="EW")
self.key.bind("<Return>", self.OnPressEnter)
self.entryVariable.set(dictFields[key])
i+=2
Any help would be appreciated!
EDIT:
I am unsure if the downs are because my questions wasn't clear (as they are embedded within comments in the code) or if the question itself is generally a bad one. Would appreciate if someone could kindly advise.
Questions:
How can I create class variables dynamically? I have googled a few methods that involved setattr(self, key, 0) or "{0}".format(key). However, I cant seem to get them to work as I am trying to assign a tkinter.Entry object to this dynamically created class variable name.
Thanks again.

self is great, if you are in something that can call itself a self.
For this you will need a class so you can put these variables in one place and make sense of this thing you are building.
how about:
class calculator(<you_might_need_another_class_here>):
""" just a normal calculator """
Then your next line would define how you want to start the calculator
def __init__(self): #here is where self shows up
pass
#replace "pass" with anything calculators do automatically
After this point your use of self will work inside this class.
yes you can dynamically create attributes with setattr. Make sure all your keys are strings.
But the last question is No. You are accessing "self.key" instead of self.S0
so a line like
self.key = tkinter.Entry(self, textvariable=self.entryVariable, justify="center")
can become
setattr(self,key,tkinter.Entry(self, textvariable=self.entryVariable, justify="center")
but quite honestly I don't know what to do with this line.
self.key.grid(row=i, column=1, sticky="EW")
you can use getattr(self,key) instead to pull the dynamically created self.keys

Related

TKinter syntax to creat widget

Is this written technically ok?
Will it cause any problems. I cannot find any info about this way of constructing code for tkinter, however it works..
myButton = tkinter.Button(main_window)
myButton['text'] = "Click Me!"
myButton['state'] = "normal"
myButton['command'] = myClick
myButton['fg'] = "blue"
instead of:
myButton = tkinter.Button(main_window,text="Click Me!",state="normal", command=myClick,fg="blue")
If someone think why, just because code looks neater to me.
What you have written will work but if your don't like the presentation of the standard syntax, you could always do something like this:
myButton = tkinter.Button(main_window,
text="Click Me!",
state="normal",
command=myClick,
fg="blue")
According to the docs it is a legitim way to configure your widgets.
The reason why this works is because of the mapping protocol that is used by tkinter. As an example you can see here how it works and there is no danger in usage of it:
class Unpackable(object):
def __init__(self):
self.options=['hello','world']
def keys(self):
return self.options
def __getitem__(self, key):
return dict(zip(self.options,'first_word last_word'.split()))[key]
unp = Unpackable()
print(unp['hello'])
Output:
first_word
The offical python documentation says to setting options:
Options control things like the color and border width of a widget.
Options can be set in three ways:
At object creation time, using keyword arguments
fred = Button(self, fg="red", bg="blue")
After object creation, treating the option name like a dictionary
index
fred["fg"] = "red"
fred["bg"] = "blue"
Use the config() method to update multiple attrs subsequent to object
creation
fred.config(fg="red", bg="blue")
This is correct.i mean to say you have just defined variable of button then you are adding attributes of button
In following way all attributes are called in one statement only.
myButton = tkinter.Button(main_window,text="Click Me!",state="normal",command=myClick,fg="blue")
But you have called all attributes by variable.it just take more Lines.
And technically what you have written is fine

Issue with getting value of checkboxes tkinter

I'm trying to get the value of checkboxes in tkinter so I can have the data from those checkboxes exported into a excel spreadsheet
I've tried having the checkboxes generate iteratively (as they are presently) and making them manually, but no matter what I do, can't get them to give me their values (whether they are checked or not) and it's really stressing me out.
def check():
for movie in movies():
print(button.get())
Button(moviewindow,text="Check",command=check).pack(anchor=S)
for movie in movies():
var1 = IntVar()
button = Checkbutton(moviewindow,
bg=moviewindow_bg,
font=("Times",23),
onvalue=1,
offvalue=0,
text=movie.replace("<strong>","").replace("</strong>",""),
variable=var1).pack(anchor=W)
I expect the code to print either 1 or 0, but I cant get the checkboxes to return their values.
You need to store your variables (and possibly, buttons) somewhere. What's happening currently is this:
You create a Button that runs the function check. Inside check, you iterate over movies, and try to get the value from button. You should be getting the value from var1, but that's not the chief issue here. The main problem is that var1 only contains the very last IntVar created, so the loop will only repeat the value of the last checkbox you created.
Without knowing what kind of an object movie is, it's hard to say the best way to proceed here. If movie is a class object, you could perhaps change its properties. If it's a hashable object, here's what you can do.
Somewhere above your code: Create a dict for association between movies and vars
checkbox_vars = {}
Fix check to use this dict
def check():
for movie in movies():
print(checkbox_vars[movie].get())
Within your loop, store the variable in the dict
var1 = IntVar()
# Store the variable in checkbox_vars, for this specific movie
checkbox_vars[movie] = var1
This is still a somewhat inelegant way to do it, but it should illustrate how you need to actually create an association between button/var1 and movie yourself instead of it being implicit.
Use var1.get() to get the current value of the checkbox.
To print the value as soon as the check button is updated, use it inside the button's invoke method.

Python - Can I use a lambda function to generate a repetitive GUI with tkinter? (labels/text/buttons)

I have a very repetitive GUI to implement - think tens of label/text/button fields, for a data entry form. The sizes of each repeated section can be the same - in fact, everything can be the same except the text in the label and the variable that the data from the text field is assigned to upon completion.
I've worked with an engineer who used lambda functions to generate sub-functions in a very complex way which I almost followed, but not quite 100%. I was hoping, since this is a similar, mostly repetitive task, that there was some way to use a formulaic function to repeat the GUI creation work for me, rather than to have to type out each and every GUI item.
Is it possible to have repetitive GUI elements generated by a function, and if so, is that a lambda function? Or is there a different (or better) way to accomplish the same "not repeating myself"?
Is it possible to have repetitive GUI elements generated by a function, and if so, is that a lambda function?
Yes, it's possible to create gui elements with a function, and no, it's not a lambda function. Not only is it possible, it's arguably a best practice to create gui elements in a function, though you could also just use a simple loop or a conventional function.
When creating groups of widgets that are somehow tied together, it's even better to create a custom class that can encapsulate all of the behavior and provide a simple interface for the rest of the program.
Example
In the following example, we want to create a series of widgets with a label, an entry, and a submit button. It is going to be implemented as a class, since we are in effect creating an object that represents one form field.
Note: the following code assumes you imported tkinter with import tkinter as tk.
First, lets create a callback that sets a value in a dictionary, and also prints out the value for debugging purposes. The callback will be given the name of the field, and the value that the user entered:
data = {}
def handle_submit(name, value):
print("you submitted '%s' for %s" % (value, name))
data[name] = value
Next, the code to create 10 items would might look like this:
root = tk.Tk()
for i in range(1, 11):
field_name = "field %s" % i
row = CustomWidget(root, name=field_name, callback=handle_submit)
Finally, we need to create our class. We inherit from tk.Frame so that we can use it like any other tkinter widget. It needs to take parameters for its parent, the field name, and the function to call when the user presses submit:
class CustomWidget(tk.Frame):
def __init__(self, parent, name, callback):
tk.Frame.__init__(self, parent)
self.name = name
label_text = name.title() + ":"
self.callback = callback
self.label = tk.Label(self, text=label_text, anchor="e")
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Submit", command=self.submit)
self.button.pack(side="right")
self.label.pack(side="left")
self.entry.pack(side="left", fill="x", expand=True)
def submit(self):
self.callback(self.name, self.entry.get())

How to display a leading zeros for number in wx.SpinCtrl?

What's the best way to dispay leading zeros for number in wx.SpinCtrl?
I've created SpinCtrl:
self.MySpin = wx.SpinCtrl(self, min=1, max=100)
and i want that displaying 002, 003... 012 etc
when i press up button in this spin
How I can do this?
I don't think there is any way to do this, you'd need to use wxSpinButton bound to a wxTextCtrl manually.
I don't believe it's supported by wxPython. You would have to roll your own widget or modify an existing one. I would look at FloatSpin since it is pure Python. It would be a lot easier to hack than wx.SpinCtrl since SpinCtrl is a wrapped C++ widget.
For solving my problem i've changed widget wx.SpinCtrl to wx.SpinButton (as recommend user VZ. in this answer) and i've created new class LeadingSpinButton inherited from wx.SpinButton and add GetLeadingValue method, leading_width and leading_char attributes.
class LeadingSpinButton(wx.SpinButton):
def __init__(self, parent, style, leading_width=0, leading_char='0'):
wx.SpinButton.__init__(self, parent=parent, style=style)
self.leading_width = leading_width
self.leading_char = leading_char
def GetLeadingValue(self):
"""GetLeadingValue(self) -> str"""
value = str(self.GetValue())
value = value.rjust(self.leading_width, self.leading_char)
return value
How is my solution?

dynamically asigning instances in Python/PyQt4

Ok, this might be a duplicate, but as I couldn't really get anything out of (possibly) similar questions, here is mine: I'm working on a small PyQt4 program where I can enter the name of a song in a QLineEdit and then add a QLabel of it beneath it. I want a button beside each one of these labels that deletes the label when clicked. Relevant code:
def Add(self):
self.rf= QtGui.QLabel(self.le1.text(),self)
self.rf.move(45,30)
self.rf.resize(450,30)
self.rf.show()
self.x = QtGui.QPushButton("X",self)
self.x.move(10,30)
self.x.resize(30,30)
self.x.show()
self.x.clicked.connect(self.Del)
def Del(self):
self.rf.close()
self.x.close()
Now, what I'm not understanding is how I can assign a different instance to each of these dynamically added Qlabels, in order to delete the specific one when the button is clicked.
The best idea I had was creating a variable containing a number that would change with each added QLabel, something like var = rf+str(num) and num = 0, then adding 1 to num for each QLabel and then using getattr for the instances, so getattr(self, var) = Qtgui.QLabel(...), which unfortunately gives me an error that I can't assign that value to the function. And I can't create a dictionary since I have to have different instances for that.
Any ideas would be highly appreciated, thanks a lot.
You could keep them all in a dict and then key that off of the label text. It also provides a quick way to check for duplicates.

Categories