I have Tkinter main class with a notebook:
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.color_widget = '#1B608E'
self.notebook = ttk.Notebook(self)
self.Page1 = Page1(self.notebook)
self.Page2 = Page2(self.notebook)
self.notebook.add(self.Page1, text='PCE Franchisés')
self.notebook.add(self.Page2, text='PVC Franchisés')
For each page of the notebook, I have a class defined as container:
class Page1(ttk.Frame):
def __init__(self, container):
super().__init__(container)
color = MainApplication.color_widget
self.label_INPUT = tk.Label(self, text='Settings', color=color,
)
self.label_INPUT.place(relx=0.03, rely=0.04)
class Page2(ttk.Frame):
def __init__(self, container):
super().__init__(container)
In each Page I want get the value of the variale color_widget defined in Main class. I tried MainApplication.color_widegt but it didn't work.
The simple way is to pass the instance of MainApplication to those pages, so that those pages can access the instance variable color_widget via the passed instance:
import tkinter as tk
from tkinter import ttk
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('800x600')
self.color_widget = '#1B608E'
self.notebook = ttk.Notebook(self)
self.notebook.pack(fill='both', expand=1)
self.Page1 = Page1(self.notebook, self) # pass instance of MainApplication as well
self.Page2 = Page2(self.notebook, self)
self.notebook.add(self.Page1, text='PCE Franchisés')
self.notebook.add(self.Page2, text='PVC Franchisés')
class Page1(ttk.Frame):
# added controller argument
def __init__(self, container, controller):
super().__init__(container)
self.controller = controller
# access MainApplication.color_widget
color = controller.color_widget
self.label_INPUT = tk.Label(self, text='Settings', fg=color)
self.label_INPUT.place(relx=0.03, rely=0.04, anchor='nw')
class Page2(ttk.Frame):
def __init__(self, container, controller):
super().__init__(container)
self.controller = controller
MainApplication().mainloop()
Related
I have this code below, and I want to pass variable n in class Data to be used in class tab1 and be used as the textvariable of entry1. however, I get this error:
AttributeError: 'dict' object has no attribute 'n'
or in general, I want to be able to pass variables between tabs.
import tkinter as tk
from tkinter import ttk
class Data:
def __init__(self):
self.n = tk.IntVar()
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.minsize(400, 400)
self.maxsize(400, 400)
self.my_notebook = ttk.Notebook(self)
self.my_notebook.pack(fill="both", expand=True)
self.app_data = {}
lst = [tab1, tab2, tab3]
for N, F in enumerate(lst):
tab = F(self.my_notebook, self.app_data)
self.my_notebook.add(tab, text="Tab"+str(N+1))
self.data = Data()
class tab1(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
entry1 = tk.Entry(self, textvariable=data.n)
entry1.pack()
class tab2(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
label2 = tk.Label(self, text="Tab2")
label2.pack()
class tab3(tk.Frame):
def __init__(self, parent, data):
super().__init__(parent)
self.data = data
label3 = tk.Label(self, text="Tab3")
label3.pack()
app = SampleApp()
app.mainloop()
You never create an instance of Data. Instead, you're initializing self.data to an empty dictionary. You need to create an instance of Data and pass that to the tabs.
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
...
self.app_data = Data()
...
I have a main class for my gui and I added a ttk.NoteBook after a label:
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('1000x500')
self.configure(background='#F0F8FF')
#TOP LABEL
load = Image.open("my_image")
load = load.resize((200, 67), Image.ANTIALIAS)
self.render = ImageTk.PhotoImage(load)
self.Label_top = tk.Label(self, image=self.render, compound=tk.LEFT, text="TOOL")
self.Label_top.pack()
#--Notebook---------
self.notebook = ttk.Notebook(self)
self.Page1 = Page1(self.notebook)
self.Page2 = Page2(self.notebook)
self.Page3 = Page3(self.notebook)
self.Page4 = Page4(self.notebook)
self.notebook.add(self.Page1, text='PAGE1')
self.notebook.add(self.Page2, text='PAGE2')
self.notebook.add(self.Page3, text='PAGE3')
self.notebook.add(self.Page4, text='PAGE4')
self.notebook.pack(fill='x', side=TOP)
#expand=True create empty space between my top label and my notebook, even with side=TOP
And I defined each frame in a class like this :
class Page1(ttk.Frame):
def __init__(self, container):
super().__init__()
self.(width=400, height=280) #Error message
self.pack(expand=True) #Doesn't work
Do you know how can I expand my frame for that it fills my page and pack the notebook just after my top label
I think this will do what you want. I've incorporated most of the things #Bryan Oakley mentioned in his answer except I also added a BasePage class and derived all the other Page classes from it. This was done to provide a place to put code that would otherwise need to be repeated each of the subclasses.
I also changed some of your variable names to conform to PEP 8 Naming Conventions.
import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *
class BasePage(ttk.Frame):
def __init__(self, container):
super().__init__(container, width=400, height=280)
classname = type(self).__name__
tk.Label(self, text=f'Welcome to {classname}').place(relx=0.5, rely=0.25,
anchor=CENTER)
class Page1(BasePage):
def __init__(self, container):
super().__init__(container)
class Page2(BasePage):
def __init__(self, container):
super().__init__(container)
class Page3(BasePage):
def __init__(self, container):
super().__init__(container)
class Page4(BasePage):
def __init__(self, container):
super().__init__(container)
class MainApplication(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('1000x500')
self.configure(background='#F0F8FF')
#--Notebook---------
self.notebook = ttk.Notebook(self)
self.page1 = Page1(self.notebook)
self.page2 = Page2(self.notebook)
self.page3 = Page3(self.notebook)
self.page4 = Page4(self.notebook)
self.notebook.add(self.page1, text='Page1')
self.notebook.add(self.page2, text='Page2')
self.notebook.add(self.page3, text='Page3')
self.notebook.add(self.page4, text='Page4')
self.notebook.pack(expand=True, fill=BOTH)
app = MainApplication()
app.mainloop()
I see three problems.
First, each "page" needs to be a child of the notebook. You do that by making sure the notebook is passed to the __init__ of the frame:
class Page1(ttk.Frame):
def __init__(self, container):
super().__init__(container)
Second, you need to not call pack on the page. self.notebook.add is already adding the frame to the notebook. So, remove the line self.pack(expand=True) from each page.
Third, self.(width=400, height=280) needs to be self.configure(width=400, height=280)
I'm starting to work with classes with a Tkinter app, but I don't seem to understand how classes work, especially the relationship parent-controller. As you can see in the code down below, I was planning to have an outer class for a whole section, then 4 inner classes for every section within that frame. However, I cannot call those classes from the initial frame. Is there any better way to do this? What is it that I'm doing wrong?
class MainScreenFrameCenter(tk.Frame):
def __init__(self, parent, controller,*args,**kwargs):
tk.Frame.__init__(self,parent, bg="white",height=680, width=640,highlightbackground="black", highlightthickness=1)
self.controller = controller
self.pack(side="top", fill="both", expand=True)
self.widgets_nw = MainScreenFrameCenterNW(parent=self,controller=self)
self.widgets_sw = MainScreenFrameCenterSW(parent=self,controller=self)
self.widgets_ne = MainScreenFrameCenterNE(parent=self,controller=self)
self.widgets_se = MainScreenFrameCenterSE(parent=self,controller=self)
class MainScreenFrameCenterNW(tk.Frame):
def __init__(self, parent, controller,*args,**kwargs):
tk.Frame.__init__(self,parent,height=350,width=640,bg="white",highlightbackground="black",highlightthickness=1)
self.controller = controller
self.grid(row=0,column=0,sticky="nsew")
class MainScreenFrameCenterSW(tk.Frame):
def __init__(self, parent, controller,*args,**kwargs):
tk.Frame.__init__(self,parent,height=350,width=640,bg="white",highlightbackground="black",highlightthickness=1)
self.controller = controller
self.grid(row=1,column=0,sticky="nsew")
class MainScreenFrameCenterNE(tk.Frame):
def __init__(self, parent, controller,*args,**kwargs):
tk.Frame.__init__(self,parent,height=350,width=640,bg="white",highlightbackground="black",highlightthickness=1)
self.controller = controller
self.grid(row=0,column=1,sticky="nsew")
class MainScreenFrameCenterSE(tk.Frame):
def __init__(self, parent, controller,*args,**kwargs):
tk.Frame.__init__(self,parent,height=350,width=640,bg="white",highlightbackground="black",highlightthickness=1)
self.controller = controller
self.grid(row=1,column=1,sticky="nsew")
You need to move all of the class definitions to global scope by putting them all at the same level of indentation.
class MainScreenFrameCenter(tk.Frame):
...
class MainScreenFrameCenterNW(tk.Frame):
...
class MainScreenFrameCenterSW(tk.Frame):
...
class MainScreenFrameCenterNE(tk.Frame):
...
class MainScreenFrameCenterSE(tk.Frame):
...
It seems you are attempting to make a small grid. Classes are generally not nested inside of another class. If you create a class that represents 1 grid cell, you can use a loop to create a grid from it.
import tkinter as tk
class Cell(tk.Frame):
def __init__(self, master, column:int=0, row:int=0, **kwargs):
kwargs = {**{'bg':'white', 'highlightbackground':'black','highlightthickness':1}, **kwargs}
tk.Frame.__init__(self, master, **kwargs)
self.grid(column=column, row=row, sticky='nswe')
class App(tk.Tk):
def __init__(self, **kwargs):
tk.Tk.__init__(self)
self.configure(**kwargs)
cols = 2
for i in range(cols):
self.grid_columnconfigure(i, weight=1)
rows = 2
for i in range(rows):
self.grid_rowconfigure(i, weight=1)
for i, c in enumerate(['nw', 'ne', 'sw', 'se']):
self.__dict__[f'widgets_{c}'] = Cell(self, i%cols, i//cols)
self.widgets_nw['background'] = 'red'
if __name__ == '__main__':
root = App(background="white", highlightbackground="black", highlightthickness=1)
root.geometry('640x680+300+300')
root.title('not Can is Should Example')
root.mainloop()
I'm trying to display a simple Notebook widget with two tabs. Here's the code of what I tried without all the unnecessary code in between:
import tkinter as tk
from tkinter import ttk
color_bg = "gray20"
font = "Lucida Sans Typewriter"
DEFAULT_FONT_SIZE = 16
class Tab(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.textfield = tk.Text(
self.parent,
font = (font, DEFAULT_FONT_SIZE),
background = color_bg,
bd = 10,
relief = tk.FLAT)
self.textfield.pack(fill = tk.BOTH, expand = True)
class TabDisplay(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack(fill = tk.BOTH, expand = True)
self.tabholder = ttk.Notebook(parent)
self.tabholder.pack(fill = tk.BOTH, expand = True, side = tk.TOP)
self.viewtab = Tab(self.tabholder)
self.edittab = Tab(self.tabholder)
self.tabholder.add(self.viewtab, text = "View")
self.tabholder.add(self.edittab, text = "Edit")
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
# Set size and position
self.x_pos = 0
self.y_pos = 0
self.width = self.parent.winfo_screenwidth()
self.height = self.parent.winfo_screenheight()
self.parent.geometry(f"{self.width}x{self.height}+{self.x_pos}+{self.y_pos}")
self.parent.resizable = True
# Add Widgets
self.tabdisplay = TabDisplay(self)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(fill = tk.BOTH, expand = True)
root.mainloop()
When I run this, it does not display an actual notebook. It just displays the two tabs below eachother. However, when I replace Tab(self.tabholder) with tk.Frame(self.tabholder) it functions perfectly (apart from not using the contents of the Tab() class).
Why does it not display correctly with my Tab() class? I have never had issues with classes that inherit from tk.Frame until I started using ttk.Notebook(). Is it an issue with ttk?
EDIT: I have since found out that the actual culprit is the Text widget within the Tab() class. Now my question is why does adding a widget break the notebook?
Think of a frame like a box. You create a box, and then you put widgets inside the box. Only, in your case you're creating widgets in the box but placing them outside the box when you add them to self.parent rather than self.
When you create a class that inherits from tk.Frame, every widget inside that class should be a direct child or descendant of self. In doing so, an instance of the class becomes a self-contained object that can be the child of any other widget.
For example:
class Tab(tk.Frame):
def __init__(self, parent):
...
self.textfield = tk.Text(
self,
...
... and:
class TabDisplay(tk.Frame):
def __init__(self, parent):
...
self.tabholder = ttk.Notebook(self)
...
I'm creating an GUI interface for my own project in python with tkinter library.
On one frame i setted an tk.Entry for me to write some text.
I want to use this information in the next Frame (let's say in a tk.Label to be simple)
But i can't reach the information, seems to be because the two function belong to differents class.
Tried to make private_key global but seems to overwrite in the definition.
Tried to return private_key but i still can't access it because i can't call the parameter in the next class.
Tried to use the function again in the next class, same problem.
Tried to set the label in the PVK class, doesn't seems to work either
from tkinter import *
# type and size of font
LARGE_FONT = ('MS Serif', 15)
# white writing color
FRONT_COLOR = '#ffffff'
# dark_gray background color
BACKGROUND_COLOR = '#272727'
class Bobby(Tk):
# Used each time the function is called
def __init__(self):
# init tkinter
Tk.__init__(self)
Tk.iconbitmap(self, default='bobby.ico')
Tk.wm_title(self, "Bobby")
Tk.geometry(self, '500x200')
container = Frame(self)
container.pack(side='top', fill='both', expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for f in (PVK, Display):
frame = f(container, self)
self.frames[f] = frame
frame.grid(row=0, column=0, sticky=NSEW)
self.show_frame(Welcome)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class PVK(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.configure(background=BACKGROUND_COLOR)
here = Label(self, text="here", font=LARGE_FONT, background=BACKGROUND_COLOR, fg=FRONT_COLOR)
here.grid()
self.pvk = Entry(self, show=" ")
self.pvk.bind('<Return>', self.check)
self.pvk.grid()
def check(self, event):
private_key = int(self.pvk.get()), 11413
bobby.show_frame(Display)
return private_key
class Display(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.configure(background=BACKGROUND_COLOR)
#want to display it here
Label(self, text=str(PVK.private_key)).grid()
bobby = Bobby()
bobby.mainloop()
My expect is to display the label with the text in it which would mean i can use the variable.
I currently get the error that private_key isn't defined.
Since you pass controller into the __init__() of Display, then you could find the instance of the PVK class:
class PVK(Frame):
def __init__(self, parent, controller):
# stuff omitted
def check(self, event):
self.private_key = int(self.pvk.get()), 11413 # note self.private_key
bobby.show_frame(Display)
class Display(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
self.configure(background=BACKGROUND_COLOR)
#want to display it here
private_key = controller.frames[PVK].private_key
Label(self, text=str(private_key)).grid()
There will be other ways to access private_key, eg PVK could write it back to the controller: self.controller.private_key = private_key etc