This code is part of the main calculator application mentioned here: https://pastebin.com/ECA2AQzY
I am new to python and from my understanding, "self" is the first argument being passed automatically for each instance of a class. Here, I am confused whether "app" would be self? and Frame is some superclass?
I do not understand why init is called on Frame within a main init method?
Also, why is lambda needed? Can it just not be command = self.appendToDisplay("7"))
What was the need of calling grid method on "app" object when the grid is called in the class itself?
calculator = Tk()
calculator.title("Calculator")
calculator.resizable(0, 0)
#================================================
class Application(Frame):
def __init__(self, master, *args, **kwargs):
Frame.__init__(self, master, *args, **kwargs)
self.createWidgets()
#================================================
def createWidgets(self):
self.sevenButton = Button(self, font=("Helvetica", 11), text="7",
borderwidth=0, command=lambda: self.appendToDisplay("7"))
self.sevenButton.grid(row=1, column=0, sticky="NWNESWSE")
#================================================
app = Application(calculator).grid()
calculator.mainloop()
Let's try to answer your questions in order:
Application intherrits from Frame, that is all methods implemented in Frame are also avaliable in Application. In addition Application can implement its own methods as well as override implementations in Frame. In the latter case, the implementations in Application replace the implementations in Frame. This is the case for the __init__() method. The method in Application replaces the one in Frame. Thus, the Frame() __init__() method is never called and that is typically not good, since there may be a lot of the functionallity in Frame() that needs explicit initialization. Therefore, Application.__init__() must explicitly call Frame.__init__() to get it to initialize.
The difference between self.appendToDisplay("7") and lambda: self.appendToDisplay("7") is that in the first case the method appendToDisplay() is directly called and the result is returned. When using lambda instead of calling the function, a new function is defined. Thus, when using the lambda construct the result will not be directly available. Instead the result of the calculation will be delayed to a later time, when the command is to be executed. Thus, we want to display 7 on the display when the button is pressed and not now.
The grid call in Application.createWidgets() refers to the button, to place that in the calculator Application. The Application.grid() call on the other hand, is placing the whole calculator itself. Thus, it is a hierachical thing, where the parts of the application are first placed and then the application itself.
I hope the above answers help you understand the concepts a bit better. It may be quite a bit to digest and you should try to find a few tutorials that explains the concepts in a bit more detail.
Related
Thankful for any assistance in my question.
I am using Python 3 with tkinter where I have setup multiple pages, however I am trying to call a specific textvariable which is in a class, within a definition and wondering if it's possible to do with the way I built my script.
It's quite a big script so I don't want to paste all the code here, I hope this will be enough.
Basically the class is called POIIN(tk.frame) and I am trying to call
class POPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.entryboxes()
def entryboxes(self):
# Entry boxes + variables
global voucher
voucher = IntVar()
entry_box3 = tk.Entry(self, textvariable=voucher, width=32, bg="lightgreen")
entry_box3.place(x=435, y=151)
I am trying to call the textvariable by doing the following outside of the class:
def do_mssql():
try:
if int(POPage.entryboxes(voucher).get()) > int(maxN):
voucherchktext()
elif int(voucher.get()) < int(minN):
voucherchktext()
else:
sql()
except:
vouchermissing()
However it seems to be calling the IntVar() and I don't get any error message, (quite new with Python)
Basically I can pull the voucher from another class (page) with just doing the int(voucher.get()).
I could of course rename the textvariable and call it that way (which I don't mind), but it would be great if there is any way around this.
Greatful for any help!
BR,
Thanks for posting your entire code on pastebin. By looking at your code I can see that none of your classes actually take ownership of the tkinter widgets they create. In otherwords, they don't have any instance variables. I would suggest a radical redesign and getting rid of all global variables and objects - in this case it's a major code smell and suggests that there is a flaw in your design - and that's fine, you did say you were a beginner after all. I see a lot of good things in your code, too! Not to be one-sided...
Basically, the change I'm recommending is this: Each frame class should take ownership of the widgets they create. That includes things like tk.Labels, tk.Buttons, tk.Entrys and tk.IntVars among others.
The major benefit this provides, is that any instance of your frame class will have its own tk.IntVar (or whatever entry widget variable, like tk.StringVar) which it does not need to share with any of the other classes (you called it a textvariable). This makes sense if you think about it: Every tk.Entry widget is coupled with one variable object that keeps track of the user's entered data for that entry widget - if you create just one global entry variable and share it with all entry widgets in all frames, you could easily lose a user's entered data - there's really no reason to do that.
Try running/playing around with the following code snippet:
import tkinter as tk
class MyFrame(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.label = tk.Label(self, text="Some Text")
self.label.pack()
self.entry_var = tk.IntVar()
self.entry = tk.Entry(self, textvariable=self.entry_var, width=32)
self.entry.pack()
self.button = tk.Button(self, text="Print this frame's 'entry_var'", command=lambda: print(self.entry_var.get()))
self.button.pack()
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Title")
self.geometry("256x256")
self.resizable(width=False, height=False)
self.frames = []
for frame in MyFrame(), MyFrame():
self.frames.append(frame)
frame.pack()
def main():
application = Application()
application.mainloop()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
You have a main Application (you call it Ghost in your example), which has multiple frames (my example only has one frame class, but the Application has two instances of that frame class). Each frame instance has an entry widget and a unique, completely separate and distinct corresponding entry variable object. There is no reason for frames to share the same entry variable object. Making the widgets and entry variable objects instance variables of their classes should eliminate your use of global variables entirely.
In my example, if I needed to get access to the values in the entry widgets from outside the classes - like in the main function for example, you would say application.frames[0].entry_var.get() to get the value in the first frame's entry widget.
As the question states, I can't seem to fully grasp the point of using classes with tkinter.
I have read through a decent number of different sites but I keep getting search results on how to create and use classes, but none so far have been able to get through to me. I've even scoured through the suggested questions while asking this one. The closest I've come to understanding is Bryan's explanation on this answer to the question Why use classes when programming a tkinter gui?
But still, I feel like I'm almost there, just not quite over the edge of understanding.
In his example in the link, he creates an unconventional program, and then a better, more conventional program that does the same thing. I know that it represents a much smaller scale than the thousand-line programs that could really benefit from an object oriented approach.
Does every widget need to be in its own separate frame that's maybe part of an even bigger frame?
Can classes have methods that create and place a frame? In addition, can those same classes have methods than can create, modify, and place a widget within the previously made frame?
I also have some code that allows me to create, modify, and place a widget. Although I know it's not conventional, so I would greatly appreciate some input on this as well. Any suggestions on what you would do with this code to make it better?
import tkinter as tk
def layout(self, row=0, column=0, columnspan=None, row_weight=None, column_weight=None, color=None, sticky=None, ipadx=None, padx=None, ipady=None, pady=None):
self.grid(row=row, column=column, columnspan=columnspan, sticky=sticky, ipadx=ipadx, padx=padx, ipady=ipady, pady=pady)
self.grid_rowconfigure(row, weight=row_weight)
self.grid_columnconfigure(column, weight=column_weight)
self.config(bg=color)
class MyLabels(tk.Label):
def __init__(self, parent, text, **kwargs):
tk.Label.__init__(self, parent, text=text)
layout(self, **kwargs)
class MyButtons(tk.Button):
def __init__(self, parent, text, command, **kwargs):
tk.Button.__init__(self, parent, text=text, command=command)
layout(self, **kwargs)
window = tk.Tk()
test_button = MyButtons(window, "hi", None, color="pink")
window.mainloop()
Edited after comments:
So I've been working many hours since yesterday trying to incorporate the ideas you've had for me. This is what I came up with:
import tkinter as tk
window = tk.Tk()
class MyWidgets(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.layout()
def layout(self):
self.grid(row=0, column=0)
self.config(bg="blue")
class MyButtons(MyWidgets):
def __init__(self, parent, text):
MyWidgets.__init__(self, parent)
tk.Button(parent, text=text)
self.layout()
frme = MyWidgets(window)
btn = MyButtons(frme, text="Test")
window.mainloop()
I've tried moving things around and rewriting lots of areas on this little side program, and even though I was able to prove that btn is infact accessing the self.config(bg="blue") attribute, the button doesn't appear to change. As a matter of fact I can't find a way to make the button appear without needing to put self.grid() in the child class just after the button is created.
Still, even if I did add the self.grid() the button still won't turn blue. Is it something with self?
Why won't the button appear when the child class creates it, and the parent class places it?
Note: I've purposefully omitted the entire layout function and replaced it with just a simple config method. I figure if I can understand this, I can then find a way to incorporate the whole function back into the code.
Does every widget need to be in its own separate frame that's maybe part of an even bigger frame?
That's a bit like asking if every part of a mathematical expression needs parenthesis. Strictly speaking, the answer is "no". However, using frames to organize groups of widgets is a tool designed to make writing and understanding the code easier, much like parenthesis in a complex math equation makes writing and understanding the equation easier.
Can classes have methods that create and place a frame? In addition, can those same classes have methods than can create, modify, and place a widget within the previously made frame?
Yes, and yes. Methods in a class don't have any limitations on what they can and cannot do. Methods of a class can do anything that a normal function can do.
I also have some code that allows me to create, modify, and place a widget. Although I know it's not conventional, so I would greatly appreciate some input on this as well. Any suggestions on what you would do with this code to make it better?
"Better" is highly subjective. What is better for 20 lines of code might not be better for 200, 2,000, or 20,000. What is better for a function used exactly twice might not be better for a function used hundreds or thousands of times (or visa versa).
That being said, you're doing one thing that is very unconventional and which leads to making your code harder to understand: you're using self as a parameter for a function that is not a method of a class. self means something very specific to python programmers; using it outside of the context of a method is very confusing.
You should do one of two things for the method layout:
Rename self to be widget or any other term other than self
Create a base class that defines layout, and then have your classes inherit from the base class. In that case, self is the proper first argument.
This part of the answer refers to code which was added after I wrote my original answer.
The base class I was referring to needs to be a separate class. For example:
class Base():
def layout(self):
self.grid(row=0, column=0)
self.config(bg="blue")
class MyLabels(Base, tk.Label):
def __init__(self, parent, text, **kwargs):
tk.Label.__init__(self, parent, text=text)
self.layout(self, **kwargs)
class MyButtons(Base, tk.Button):
def __init__(self, parent, text, command, **kwargs):
tk.Button.__init__(self, parent, text=text, command=command)
self.layout(self, **kwargs)
This type of class is sometimes called a mixin because it's not designed to be instantiated as a standalone object. Rather, it "mixes in" some additional behavior to other classes. A mixin will typically have methods, but won't have its own __init__.
Inspired by this 300+ vote closed Q&A: Best way to structure a tkinter application?, I'm looking to avoid explicitly using root in a function within a class. I think it should be implicitly declared through self or parent or something like that. Here is the code in question:
I have this code...
self.label_this = tk.StringVar()
self.label_last = tk.StringVar()
self.label_total = tk.StringVar()
tk.Label(count_frame, textvariable=self.label_this, \
font=(None, MON_FONTSIZE)).pack(anchor=tk.W)
tk.Label(count_frame, textvariable=self.label_last, \
font=(None, MON_FONTSIZE)).pack(anchor=tk.W)
tk.Label(count_frame, textvariable=self.label_total, \
font=(None, MON_FONTSIZE)).pack(anchor=tk.W)
self.update_cnt_labels()
Then later on...
''' Get list of Window ID's on monitor now '''
new_windows = self.windows_on_monitor(new_windows)
new_windows_cnt = len(new_windows) / WIN_CNT
if self.old_windows_cnt == new_windows_cnt :
FlashMessage (self.label_this, "No new windows to remove...", \
3, 750, 250)
self.update_cnt_labels()
return
Then later on...
class FlashMessage:
def __init__(self, widget, message, count=5, on=500, off=300):
self.delay_show (1, widget, message)
for i in range(count):
self.delay_show (on, widget, "")
self.delay_show (off, widget, message)
def delay_show(self, ms, widget, message):
root.after(ms, widget.set(message))
root.update_idletasks()
I want to avoid using root in the last two lines and use self or something similar.
My program call chain is something like:
the traditional: root = tk.Tk()
bunch of mainline initialization stuff.
the class: ResizingCanvas(mycanvas)
mainline function: popup(event) which is bound to <ButtonPress-1>
Dynamically formatted menu.tk_popup(event.x_root, event.y_root)
the class: RemoveNewWindows()
the function: remove()
the class: FlashMessage() (show above)
the function: self.delay_show() (shown above)
Each class and function has haphazard self, positional parameters, *args and **kwargs which mostly serve no purpose. Indeed even the __init__ above might be unnecessary. This is a result of copying code all over stack overflow.
Every second word in the program seems to be self but the word parent is only used in the class ResizingCanvas(). Do I have to propagate parent down the call list and use it somehow?
You can call after and update_idletasks on any widget. There are many such functions that can be called on any widget but which have a global effect.
In your case, you'll need to pass some widget into the FlashMessage constructor and save the reference. You can then use the reference to call the functions.
You're passing something called widget that doesn't actually contain a widget. You need to rename it to something more appropriate (eg: var), and then pass in an actual widget.
(Note: you also are calling after incorrectly, which I've fixed in the following example)
For example:
class FlashMessage:
def __init__(self, widget, var, message, count=5, on=500, off=300):
self.widget = widget
...
def delay_show(self, ...):
self.widget.after(ms, var.set, message)
self.widget.update_idletasks()
Then, whenever you create an instance of FlashMessage you need to add a widget as the first parameter.
For example, assuming that count_frame is defined in the context where you create an instance of FlashMessage and it is an actual widget, it might look something like this:
if self.old_windows_cnt == new_windows_cnt :
FlashMessage (count_frame, self.label_this, "No new windows to remove...", \
3, 750, 250)
self.update_cnt_labels()
return
I am attempting to create a ttk.notebook in python where the selection in one tab affects the selection of a widget in a separate tab. Each tab is currently set up as a different class. Is there a way to pass or call a function in one class(tab) and have it change the widget/call a function in the other class(tab)?
in short, i have two functions: lb1 and lb2 (for tk lisboxes). Ideally, I would like the selection function on lb1 to call a function to populate a list in lb2. Each are in different classes.
a general sample of what I am trying to do follows.
class One(ttk.Frame):
...
lb1 = Listbox(listvariable = apps, height = 5)
def lb2_lookup(self, *args):
#this would somehow call a function to populate lb2
self.lb1.bind('<<ListboxSelect>>', self.lb2_lookup)
class Two(ttk.Frame):
lb2 = Listbox(listvariable = lb2apps, height = 5)
Thanks a bunch. I apologize if the code sample makes no sense, but I believe it gets the general point across.
The solution is that for one class to call a function in another, it simply needs to have a reference to that class, or a reference to some sort of controller class that has a reference to the other class.
class One(ttk.Frame):
def __init__(self, master, other_class):
self.other_class = other_class
...
self.lbl.bind('<<ListboxSelect>>`, self.other_class.lb2_lookup)
two = Two(...)
one = One(..., other_class = two)
Another way to accomplish the same thing is to have the class provide an interface, so that you can connect the classes after they are created:
class One(...):
...
def set_target(self, other_class):
self.other_class = other_class
class Two(...):
...
one = One(...)
two = Two(...)
one.set_target(two)
Finally, as written your code is tighly coupled. That means that even a small change in Two might mean you have to modify class One as well. That makes for code that is hard to maintain. You should create an interface that doesn't require one class to know much about the implementation of the other class.
Specifically, in your example you are setting up a binding to call lb2_lookup. But what if you change class Two and rename lb2 to lb3? Do you really want to have to also modify One? Better to create a function in Two that doesn't directly relate to a widget. For example:
class One(...):
...
self.lb1.bind('<<ListboxSelect>>', self.other_class.lookup)
With that, you are now free to reimplement Two however you want. The only requirement is that you keep a method named lookup. However, exactly what lookup does can change as long as it works the same way.
So, for example, right now lookup could return the value from a widget named lb2, but later it could look up data from a widget named foobar. No matter what lookup does, as long as it works in the same way (takes the same arguments, returns the same type of result), you won't have to modify One whenever you change Two.
I'm a beginner at Python and I'm currently on chapter 10/12 in my book 'Python Programming for the absolute beginner'. I understand OOP in Python but due to one chapter (which discusses some relevant information about OOP) being based around a program for playing 'Cards' I had to skip part of the chapter (because I don't know how to play Cards) and so I missed out on important information which I should know at this point in the book. The reason I'm saying this is because I will need a simple answer to my question because complex answers may be too difficult for me to interpret.
But anyway here is my problem, there is a piece of a code in my book which creates a simple GUI program (which I completely understand) and then there's an Object Oriented version of the program which I do not understand (main parts highlighted in '##'s). It contains something called a 'Superclass constructor' which completely confused me (I tried doing some research on it, but it just didn't make sense to me). If anyone can help explain to me how the second version of the code works (or give helpful resources) then I will be highly grateful:
First version of the code:
from tkinter import *
# create a root window
root = Tk()
root.title("Lazy Buttons")
root.geometry("200x85")
app = Frame(root)
app.grid()
bttn1 = Button(app, text = "This is a button")
bttn1.grid()
root.mainloop()
Second version:
from tkinter import *
class Application(Frame):
def __init__(self, master): # Why is 'master' called?
super(Application, self).__init__(master) # ?
self.grid()
self.create_widgets()
def create_widgets(self):
self.bttn1 = Button(self, text = "This is a button")
self.bttn1.grid()
root = Tk()
root.title("Lazy Buttons 2")
root.geometry("200x85")
app = Application(root)
root.mainloop()
Don't skip parts of a tutorial because you don't know the problem domain - you don't need to know how to play cards to understand how card game code relates to what the program does. To your actual problem:
class Application(**Frame**):
This creates a class Application that inherits from a class Frame. If you don't know inheritance, the tutorial you're following should explain it, or you could try this introduction to classes and inheritance that I just googled up.
**def __init__(self, master):
This creates a method, with the name __init__. This is a special method in Python, which behaves similarly to a constructor in other languages - essentially, whenever an Application is created, Python will immediately call its __init__ method before it gives the new object back to whoever made it. master is just an argument, same as any other to any other function.
super(Application, self).__init__(master)
This calls the constructor of the superclass, to let it initialise the new object. super(Application, self) figures out what the superclass is (in this case it is Frame; in more complex cases that you will come to eventually, this is harder to work out and super's magic becomes important).
self.create_widgets()**
This calls the method create_widgets, which you define below this. The object before the . gets passed into the method as the first argument, self - so, this calls a different method on the same object that the method you're in has been called on.
app = Application(root)
This creates an Application - objects are created by calling the class, like how list() creates a list. The argument you pass in gets handed to __init__ as its second argument (the new Application that Python creates behind the scenes gets passed as the first argument, self, as above). Python creates a new Application, calls its __init__ for you, and then assigns the new object to the name 'app'.
'super' part here is only to properly call constructor of base class (the class you are inheriting from.)
Let's consider following example:
class Device(): # class representing some drawing device
...
def fill(self, color)
... # actual code for draw on this device
screen = Device()
WHITE_COLOR = (255, 255, 255)
class Window(object):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class ColoredWindow(Window):
def __init__(width, height, color):
super(ColoredWindow, self).__init__(width, height)
self.color = color
def fill(self):
screen.fill(self.width, self.height, self.color)
my_window = ColoredWindow(640, 480, WHITE_COLOR)
my_window.fill()
The first class here, Window, is a generic window that has width and height attributes that are passed in constructor.
ColoredWindow is a subscass of Window, it has additional attribute color.
When ColoredWindow is constructed, it needs somehow pass width and height parameters to its baseclass Window.
This is exactly what construction super(ColoredWindow, self).__init__(width, height) is for. The first part, super(ColoredWindow, self), returns a reference to baseclass (Window in our case), and second part, __init__(width, height), just calls its constructor with right parameters.
In your example master is not called, it's just parameter to __init__ method of Application class.