Questions on using ttk.Style()? - python

To make available an instance of the ttk.Style() class, it was illustrated in this tkinter guide that the syntax is:
import ttk
s=ttk.Style()
When typing these command in IDLE, I noticed that ttk.Style() actually has a predefined argument, i.e.
s=ttk.Style(master=None)
I have written the following test script:
import tkinter as tk
import tkinter.ttk as ttk
class App(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent, style='App.TFrame', relief=tk.SUNKEN,
border=10)
self.parent = parent
self.__createStyle()
self.__createWidgets()
def __createStyle(self):
self.s = ttk.Style()
self.s.configure('.', background='orange', border=100)
self.s.configure('App.TFrame', background='yellow')
self.s.configure('Btn.TButton', background='light blue', border=10)
def __createWidgets(self):
self._label = ttk.Label(self.parent, text='Label packed in root.')
self._label.pack()
self._btn = ttk.Button(self, style='Btn.TButton', command=self.__click,
text='Button packed inside self or class App, which is a ttk.Frame')
self._btn.pack()
def __click(self):
return print('Left Button Clicked!')
class myWidget(ttk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent, style='my.TFrame', relief=tk.GROOVE,
border=10)
self.parent = parent
self.__createStyle()
self.__createWidgets()
def __createStyle(self):
self.s = ttk.Style()
self.s.configure('my.TFrame', background='purple')
self.s.configure('my.TLabel', background='pink', border=10)
self.s.configure('my.TEntry', foreground='red', border=10)
def __createWidgets(self):
self._label = ttk.Label(self, style='my.TLabel',
text='myWidget Label packed in self or class myWidget, which is a ttk.Frame.')
self._label.pack()
self._entry = ttk.Entry(self, style='my.TEntry')
self._entry.pack()
if __name__ == "__main__":
root = tk.Tk()
root.title('Test Style')
root.geometry('500x150')
a = App(root)
a.pack(fill='both', expand=1)
b = myWidget(a)
b.pack()
root.mainloop()
Question 1: When do I need to declare the master arguement in ttk.Style()? E.g. in the above script, if I write self.s = ttk.Style() and self.s = ttk.Style(master=self.parent) in class myWidget, I get the same result (see Fig1).
Question 2: Is there a need to prefix s=ttk.Style() with self? I get the same result as shown in Fig1 with and without the self prefix.
Question 3: If I rename 'my.TFrame' in class myWidget as 'App.TFrame'(this name was used in class App), the background colour of the class App changed to purple color too (same color as class myWidget. Why did this happened given that variable name in different classes are unique?
Question 4: The names 'App.TFrame' and 'my.TFrame' were called before it was declared. Why did python or tkinter not complain or give an error but allowed the script to execute?
Figure 1
Figure 2

When do I need to declare the master arguement in ttk.Style()?
Probably never, except the case when tkinter doesnt support the default root. When you pass None as the master, the master becomes the current root instance of Tk class.
The main purpose of the master (root or any tk-widget) is to delegate instance of tk to the Style, so that the Style could be able to execute Tcl-related commands.
No more, no less.
Is there a need to prefix s=ttk.Style() with self?
It depends on your requirements. In context of your code - self is meaningless, because you're setting up styles in a scope of the __createStyle function.
Otherwise, if you wish to keep the reference, it makes sense to prefix with self.
If I rename my.TFrame in class myWidget as App.TFrame(this name was used in class App), the background colour of the class App changed to purple color too (same color as class myWidget. Why did this happened given that variable name in different classes are unique?
Because both of classes share the same frame style, hence the same color. Created style is a global thing, it can be chaged at runtime, and all the relevant widgets will react to these chages.
The names App.TFrame and my.TFrame were called before it was declared. Why did python or tkinter not complain or give an error but allowed the script to execute?
Why you think they should? When you pass something like <any_sensible_name>.<any_relevant_and_existing_basestyle>, ttk knows that you want a variation of a base style, so it's implicitly creates one, which inherits all base properties.
Try that trick with something more meaningless, like your current style name without dot (ttk.Frame.__init__(..., style='AppTFrame', ...)), which gives you the desired error:
_tkinter.TclError: Layout AppTFrame not found

Only a partial Answer, but I suppose #Bryan Oakley will entlighten us sooner or later.
Question 3:
If you use "App.TFrame" instead of "my.TFrame" inside your MyWidget Class, you override the predefined style properties.
Short example:
If you style "TFrame", all "TFrame"( == Tkinter.Frame/ttk.Frame ) instances will be affected.This is also sometimes referred to as "root-Style".
If you define another "somename.TFrame" and set it for one Object of type frame, it will be styles according "somename.TFrame".
Question 4:
The lookup names only override default styles. As long as they have no properties, they do not override a thing.
This "assignment" results in a tcl call and has no specific error handling inside the Tkinter / ttk Sources (used in BaseWidget class).
I can only tell that tcl does not throw an error here but I am not a tcl expert myself.
I hope this at least helps a bit.

Related

Calling textvariable from function within a class Python 3 tkinter

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.

Python Tkinter Avoid using "root" name in lower level function

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

error explanation - 'statInter' object has no attribute 'tk'

I am trying to create a simple timer in Python and am aiming to build the user interface using classes. I would like to use the classes to initialise user interface. Then in the main text of the body, I would like to add attributes using the .grid and .configure methods. But when I try to do this, the error: 'statInter' object has no attribute 'tk' appears.
I am a beginner in programming, but if I understand the error correctly the it results because the .grid and other Button methods are not inherited by my statInter (i.e. static interface) class. Is this correct? How do I solve this error? I trued inheriting the properties of Button class and even Tk class, but in the later case I get an infinite loop i.e. maximum recursion depth exceeded.
Thanks for your help
#This is a simple timer version
from tkinter import *
window = Tk()
window.title('Tea timer')
window.minsize(300,100)
window.resizable(0,0)
class statInter(Button,Entry):
def __init__(self, posx, posy):
self.posx = posx # So the variables inside the class are defined broadly
self.posy = posy
def button(self):
Button(window).grid(row=self.posx, column=self.posy)
def field(self):
Entry(window, width=5)
sth = statInter(1,2)
sth.grid(row=1, column = 2)
window.mainloop()
The problem is your derived StatInter class (CamelCasing the class name as suggested in PEP 8 - Style Guide for Python Code) doesn't initialize its base classes, which generally doesn't happen implicitly in Python (as it does in say, C++).
In order to do that from within the StatInter.__init__() method, you're going to need to know the parent widget that will contain it (all widgets except the top level window are contained in a hierarchy) — so an extra argument needs to be passed to the derived class's constructor so it can be passed on to each of the base class constructors.
You haven't encountered another problem yet, but likely will soon. To avoid it, you're also going to have explicitly pass self when explicitly calling the base class methods in button() and field().
from tkinter import *
window = Tk()
window.title('Tea timer')
window.minsize(300,100)
window.resizable(0,0)
class StatInter(Button, Entry):
def __init__(self, parent, posx, posy): # Added parent argument
Button.__init__(self, parent) # Explicit call to base class
Entry.__init__(self, parent) # Explicit call to base class
self.posx = posx # So the variables inside the class are defined broadly
self.posy = posy
def button(self):
Button.grid(self, row=self.posx, column=self.posy) # Add self
def field(self):
Entry.config(self, width=5) # Add self
sth = StatInter(window, 1, 2) # Add parent argument to call
sth.grid(row=1, column=2)
window.mainloop()
The reason you get this error is because you never invoke either of the constructors from the classes you're inheriting from (either Button or Entry).
If you change your __init__ to be:
def __init__(self, posx, posy):
Button.__init__(self)
self.posx = posx # So the variables inside the class are defined broadly
self.posy = posy
Then you won't get the error you were having previously, and a little window pops up. In the new __init__, we explicitly invoke Button's constructor.
Unlike Java and some other languages, the super constructor is NOT invoked by default. I assume that each class inheriting from other tkinter classes must have a tk field. By invoking the parent constructor of your choice, this field will be created. If you don't invoke a parent constructor, though, then this will not be an established field, and you'll get the error you have described ('statInter' object has no attribute 'tk').
HTH!

Conveying a tkinter button's execution across different classes?

I have a simple graphical user interface using tkinter which gives a blank canvas on which the cursor places a green square wherever it is clicked.
I am trying to code in a "Clear all" button at the top of the interface which clears the entire canvas (whilst retaining the cursor's ability to draw squares when it clicks on the canvas again):
import tkinter as tk
class Interface(tk.Frame):
def __init__(self, master):
super().__init__(master)
tk.Button(self, text="Clear all", command=self.clearall).pack(side = tk.LEFT)
#What the heck do I do here?
def clearall(self):
#self._canvas.delete(tk.ALL)
pass
class App(object):
def __init__(self, master):
master.title("Doodle test")
master.geometry("600x600")
self._interface = Interface(master)
self._interface.pack()
self._canvas = tk.Canvas(master, bg='white')
self._canvas.pack(fill=tk.BOTH, expand=1)
self._canvas.bind("<Button-1>", self.leftclick)
# And would I need to put something here?
def plz_run_me_when_clearall_method_is_called(self):
self._canvas.delete(tk.ALL)
def leftclick(self, arg):
d = 10
self._canvas.create_rectangle([(arg.x, arg.y), (arg.x+d, arg.y+d)], fill="green")
root = tk.Tk()
app = App(root)
root.mainloop()
Ordinarily, this would be a simple task if the entire program was constructed within a single class, e.g.:
def clearall(self):
self._canvas.delete(tk.ALL)
Since there are two classes, I cannot call the .delete(tk.ALL) method for _canvas since it is declared in the App class. So how could I convey a button to execute function X which affects item Y from another class?
EDIT: Still trying to think of an appropriate title for this post.
A simple rule of python (not just tkinter) is that, if an object needs a resource in another object, that first object needs a reference to the second object.
That means either passing in the instance of App to the instance of Interface, or passing in the canvas itself.
Using tight coupling
For example, you could pass in the canvas this way:
class Interface(tk.Frame):
def __init__(self, master, canvas):
...
self._canvas = canvas
...
def clearall(self):
self._canvas.delete(tk.ALL)
class App(object):
def __init__(self, master):
...
self._canvas = tk.Canvas(master, bg='white')
self._interface = Interface(master, self._canvas)
...
There are many ways to accomplish a similar thing. For instance, instead of passing in the canvas you can pass in the reference to the app itself (eg: self._interface = Interface(master, self). You could then either makeself._canvas` "public", or you could provide a getter that the returns the canvas.
The potential downside to the above approach is that it tightly couples the two classes together. That is, if App some day is remade to draw on an image object rather than a canvas object, Interface will also have to be modified since it depends on a specific implementation in App (namely, that it does all its work on a canvas).
Using loose coupling
Loose coupling means that Interface doesn't know or care how App is implemented, it only knows that App promises to provide an API that Interface can use. In this way, App can completely change its implementation without breaking Interface, as long as it continues to provide the same API.
To keep these classes loosely coupled, you need to move add a clearall method into App. With this, Interface doesn't need to know that App uses a canvas, it just needs to know that App provides a way to clear the drawing surface, no matter what that drawing surface is.
class Interface(tk.Frame):
def __init__(self, master, app):
...
self._app = app
...
def clearall(self):
self._app.clearall()
class App(object):
def __init__(self, master):
...
self._canvas = tk.Canvas(master, bg='white')
self._interface = Interface(master, self)
...
def clearall(self):
self._calvas.delete(tk.ALL)

Can someone please explain how the second version of this code works?

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.

Categories