Recently, I tried to find a way to paginate through listbox entries that runs functions that opens frames with forms, tabs or else, but I didn't find it.
Clearly, I wanna create a control panel application which has a side panel which can switch between pages/frames that hold widgets that user will interact with.
this is the code which I wrote to try to achieve this manner:
import tkinter as tk
from tkinter import ttk
class MainWindow() :
def __init__(self,root):
# menu left
self.menu_upper_frame = tk.Frame(root, bg="#dfdfdf")
self.menu_title_label = tk.Label(self.menu_upper_frame, text="menu title", bg="#dfdfdf")
self.menu_title_label.pack()
self.menu_left_container = tk.Frame(root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left_container, width=150, height=150, bg="red")
self.menu_left_upper.pack(side="top", fill="both", expand=True)
# create a listbox of items
self.Lb1 = tk.Listbox(self.menu_left_upper,bg ="red", borderwidth=0, highlightthickness=0 )
self.Lb1.insert(1, "Python")
self.Lb1.insert(2, "Perl")
self.Lb1.insert(3, "C")
self.Lb1.insert(4, "PHP")
self.Lb1.insert(5, "JSP")
self.Lb1.insert(6, "Ruby")
self.Lb1.bind("<<ListboxSelect>>", self.OnClick ) #return selected item
self.Lb1.pack(fill="both", expand=True, pady=50 )
# right area
self.inner_title_frame = tk.Frame(root, bg="#dfdfdf")
self.inner_title_label = tk.Label(self.inner_title_frame, text="inner title", bg="#dfdfdf")
self.inner_title_label.pack()
self.canvas_area = tk.Canvas(root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_upper_frame.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.menu_left_container.grid(row=1, column=0, rowspan=2, sticky="nsew")
self.inner_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(1, weight=1)
def OnClick(self,event):
widget = event.widget
selection = widget.curselection()
value = widget.get(selection)
# print ("selection: ",selection, ": '%s'"% value)
if value == 'Python':
self.tabtop()
def tabtop(self):
self.tabControl = ttk.Notebook(self.canvas_area, width=400)
self.tab1 = ttk.Frame(self.tabControl)
self.tab2 = ttk.Frame(self.tabControl)
self.tab3 = ttk.Frame(self.tabControl)
self.tab4 = ttk.Frame(self.tabControl)
self.tab5 = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab1, text='Login data' )
self.tabControl.add(self.tab2, text='Permission')
self.tabControl.add(self.tab3, text='Roles')
self.tabControl.add(self.tab4, text='Personal data')
self.tabControl.add(self.tab5, text='Business data')
self.tabControl.pack(expand=1, fill="both")
self.l2 = tk.Label(self.tab2, text="label 2").pack()
self.l3 = tk.Label(self.tab3, text="label 3").pack()
root = tk.Tk()
root.title("Control Panel")
root.style = ttk.Style()
root.style.theme_use("clam")
user = MainWindow(root)
root.mainloop()
If you have an idea to achieve the same manner with a different algorithm please suggest!
Related
I am using python 3.11.0a5. How do I get actually LabelFrame to draw actually size in column 1? Unfortunately, I can't figure out to work around columnspan, rowspan, etc Here is pics
Unfortunately, I don't want the LabelFrame to draw wider. Here is pics.
Here is code:
from tkinter import *
from tkinter import ttk
#Create an instance of tkinter frame
win= Tk()
#Define the geometry of the window
win.geometry("400x160")
lblChannelName = ttk.Label(win, text='Channel Name')
lblChannelName.grid(row=0, column=0, sticky='W')
entry_channelname = ttk.Entry(win, width=45)
entry_channelname.grid(row=0, column=1, sticky='W')
lblChannelTopic = ttk.Label(win, text = 'Channel Topic')
lblChannelTopic.grid(row=1, column=0, pady=2, sticky='W')
entry_channelTopic = ttk.Entry(win, width=45)
entry_channelTopic.grid(row=1, column=1, pady=2, sticky='W')
#Initialize a LabelFrame Widget
labelframe = LabelFrame(win, width=400, height=200, bd=5)
labelframe.grid(row=3, padx=2, pady=5, columnspan=18, sticky='WE')
#Checkbutton Invite
invite = IntVar()
ck1 = ttk.Checkbutton(labelframe, text ='Invite Only[+i]', variable=invite)
ck1.grid(row=0, column=0, padx=0, sticky='W')
#Checkbutton Moderated
moderated = IntVar()
ck2 = ttk.Checkbutton(labelframe, text ='Moderated[+m]', variable=moderated)
ck2.grid(row=0, padx=170, columnspan=3, sticky='W')
#Checkbutton Message
message = IntVar()
ck3 = ttk.Checkbutton(labelframe, text ='No Outside Message[+n]', variable=message)
ck3.grid(row=1, column=0, sticky='W')
#Checkbutton Private
private = IntVar()
ck4 = ttk.Checkbutton(labelframe, text ='Private[+p]', variable=private)
ck4.grid(row=1, padx=170, columnspan=3, sticky='W')
#Checkbutton Only ops
topics = IntVar()
ck5 = ttk.Checkbutton(labelframe, text ='Only ops set topics[+t]', variable=topics)
ck5.grid(row=2, column=0, sticky='W')
#Checkbutton Secret Channel
secret = IntVar()
ck6 = ttk.Checkbutton(labelframe, text ='Secret Channel[+s]', variable=secret)
ck6.grid(row=2, padx=170, columnspan=3, sticky='W')
#Checkbutton Keyed
keyed = IntVar()
ck7 = ttk.Checkbutton(labelframe, text ='Channel is keyed[+k]', variable=keyed)
ck7.grid(row=3, column=0, sticky='W')
lblkeyed = ttk.Label(labelframe, text='Key:')
lblkeyed.grid(row=3, padx=135, columnspan=2, sticky='W')
entry_keyed = ttk.Entry(labelframe, width=20)
entry_keyed.grid(row=3, padx=170, columnspan=3, sticky='W')
#Checkbutton limit
limit = IntVar()
ck8 = ttk.Checkbutton(labelframe, text ='Limit channel members[+l]', variable=limit)
ck8.grid(row=4, column=0, sticky='W')
win.mainloop()
How can I fix the Labe Frame in pics 1?
I appreciate your help.
In your code, when you grid labelframe, change the columnspan to 2. Line 24 will look like this:
labelframe.grid(row=3, padx=2, pady=5, columnspan=2, sticky='WE')
That will set your LabelFrame to the right width.
If you want the entries to align with the LabelFrame, you can make them sticky='WE'. Lines 14 and 20 would look like this:
# Line 14
entry_channelname.grid(row=0, column=1, sticky='WE')
# Line 20
entry_channelTopic.grid(row=1, column=1, pady=2, sticky='WE')
The program is made up of classes and I am trying to use a tkinter topview from within a function so that when it's called it is able to retrieve the entryfield value to the master class
from tkinter import
from PIL import Image, ImageTk
Below is the driver code handling the transitioning from one class to another
class SeaofBTCapp(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
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 (
WelcomePage, Register_new_user): # ,PageThree,PageFour):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(WelcomePage)
# def show_frame(self, cont):
# frame = self.frames[cont]
# frame.tkraise()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.update()
frame.event_generate("<<show_frame>>")
def get_page(self, cont):
for page in self.frames.values():
if str(page.__class__.__name__) == cont:
return page
return None
class Register_new_user(object):
pass
Below is the entry point of the program and is the first page to be displayed
class WelcomePage(Frame):
def __init__(self, parent, controller):
Frame.__init__(self, parent)
# self.bind("<<show_frame>>", self.main_prog)
def resize_image(event):
global photo
new_width = event.width
new_height = event.height
image = copy_of_image.resize((new_width, new_height))
photo = ImageTk.PhotoImage(image)
label.config(image=photo)
label.image = photo # avoid garbage collection
def pin_input():
top = Toplevel()
top.geometry("180x100")
top.title("toplevel")
l2 = Label(top, text="This is toplevel window")
global entry_1
global password
password = StringVar
entry_1 = None
def cleartxtfield():
global password
new = "3"
password.set(new)
# ############# Function to parse for only numerical input
def validate(input):
if input.isdigit():
return True
elif input == "":
return True
else:
return False
def enternumber(x):
global entry_1
setval = StringVar()
setval = str(x)
# print(setval)
entry_1.insert(END, setval)
entry_1 = Entry(top, textvariable=password, width=64, show='*')
entry_1.place(x=200, y=100)
entry_1.focus()
reg = top.register(validate)
entry_1.config(validate="key", validatecommand=(reg, '%P'))
def getcreds():
# check if four digit entered and is not empty
global passwd
passwd = password.get()
print(f"The Credentials are {passwd}")
def funcbackspace():
length = len(entry_1.get())
entry_1.delete(length - 1, 'end')
def killwindow():
# when the user quits it should clear all the data input fields filled in in the previous steps. and should display information that it is about to quit in a few seconds
command = top.destroy()
# Label(top,text="Goodbye\n (Closing in 2 seconds)")
top.after(2000, top.quit())
cancel = Button(top, width=8, height=3, text="Cancel", bg="red", fg="black", command=killwindow)
cancel.place(x=220, y=150)
backspace = Button(top, width=8, height=3, text="Backspace", bg="red", fg="black", command=funcbackspace)
backspace.place(x=500, y=150)
# ----number Buttons------
def enternumber(x):
global entry_1
setval = StringVar()
setval = str(x)
# print(setval)
entry_1.insert(END, setval)
btn_numbers = []
for i in range(10):
btn_numbers.append(
Button(top, width=8, height=3, text=str(i), bd=6, command=lambda x=i: enternumber(x)))
btn_text = 1
for i in range(0, 3):
for j in range(0, 3):
btn_numbers[btn_text].place(x=220 + j * 140, y=250 + i * 100)
btn_text += 1
btn_zero = Button(top, width=15, height=2, text='0', bd=5, command=lambda x=0: enternumber(x))
btn_zero.place(x=330, y=550)
clear = Button(top, text="Clear", bg="green", fg="white", width=8, height=3, command=cleartxtfield)
clear.place(x=220, y=550)
okbtn = Button(top, text="Enter", bg="green", fg="black", width=8, height=3, command=getcreds)
okbtn.place(x=500, y=550)
val = getcreds()
print("The value to be returned is %s" % val)
return val
password = pin_input()
print("Gotten password is %s" % password)
copy_of_image = Image.open("image.png")
photoimage = ImageTk.PhotoImage(copy_of_image)
label = Label(self, image=photoimage)
label.place(x=0, y=0, relwidth=1, relheight=1)
label.bind('<Configure>', resize_image)
top_left_frame = Frame(self, relief='groove', borderwidth=2)
top_left_frame.place(relx=1, rely=0.1, anchor=NE)
center_frame = Frame(self, relief='raised', borderwidth=2)
center_frame.place(relx=0.5, rely=0.75, anchor=CENTER)
Button(top_left_frame, text='REGISTER', bg='grey', width=14, height=1,
command=lambda: controller.show_frame(Register_new_user)).pack()
Button(center_frame, text='ENTER', fg='white', bg='green', width=13, height=2,
command=lambda: controller.show_frame(Register_new_user)).pack()
if __name__ == '__main__':
app = SeaofBTCapp()
app.title("Password return on topview window")
width = 1000
height = 700
screenwidth = app.winfo_screenwidth()
screenheight = app.winfo_screenheight()
alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
app.geometry(alignstr)
# app.resizable(width=False, height=False)
app.resizable(width=True, height=True)
app.mainloop()
If I understand this correctly you want to enter a password in a dialog and then get the password from the dialog when you close it.
Have a look at Dialog Windows at effbot for a discussion about creating dialog windows.
Here is an example of how you can implement a simple dialog:
from tkinter import *
from tkinter import simpledialog
class MyDialog(simpledialog.Dialog):
def body(self, master):
'''create dialog body.
return widget that should have initial focus.
This method should be overridden, and is called
by the __init__ method.'''
Label(master, text='Value:').grid(row=0)
self.e1 = Entry(master)
self.e1.grid(row=0, column=1)
return self.e1 # initial focus
def apply(self):
'''process the data
This method is called automatically to process the data, *after*
the dialog is destroyed. By default, it does nothing.'''
value = self.e1.get()
self.result = value
def validate(self):
'''validate the data
This method is called automatically to validate the data before the
dialog is destroyed. By default, it always validates OK.'''
return 1 # override
def buttonbox(self):
'''add standard button box.
override if you do not want the standard buttons
'''
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default='active')
w.pack(side='left', padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side='left', padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
if __name__ == '__main__':
root = Tk()
root.geometry('200x100+800+50')
def do():
d = MyDialog(root)
print(d.result)
b = Button(root, text='Go!', width=10, command=do)
b.pack(expand=True)
Did that answer your question?
I'm trying to create a GUI for timeseries data. It has 2 scrollable canvas and a frame within each. The top frame contains other frames within. The main_frame seems to expand with the canvas but catmainframes don't seem to do that. catmainframe is used to generate frames dynamically.
import os
import tkinter as tk
#from tkinter import ttk
from tkinter import filedialog as fd
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def on_configure(event):
# update scrollregion after starting 'mainloop'
canvas1.configure(scrollregion=canvas1.bbox('all'))
canvas2.configure(scrollregion=canvas2.bbox('all'))
def enterCategory():
global cat
global catmainframe
global rownum
global add_file
cat = category.get()
catmainframe = tk.Frame(main_frame, borderwidth=2, relief="solid")
catmainframe.grid(row=rownum,column=0,sticky='nsew', padx=3, pady=3)
#canvas1.create_window((0,0), window=catmainframe,anchor='nw')
catmainframe.grid_rowconfigure(rownum, weight=1)
catmainframe.grid_columnconfigure(0, weight=1)
catframe = tk.Frame(catmainframe, borderwidth=2, relief="solid")
catframe.grid(row=0,column=0, sticky='nsew', padx=1, pady=1)
catlabel = tk.Label(catframe, text=cat)
catlabel.grid(row=0, column=0, sticky='nsew')
add_file = tk.Button(catframe,text="Add File",command=openFile)
add_file.grid(row=0, column=1, sticky='nsew')
global catchildframe
catchildframe = tk.Frame(catmainframe, borderwidth=2, relief="solid")
catchildframe.grid(row=1,column=0,sticky='nsew', padx=1, pady=1)
catchildframe.grid_rowconfigure(1, weight=1)
catchildframe.grid_columnconfigure(1, weight=1)
global box1, box2, box3
box1 = tk.Frame(catchildframe, borderwidth=2, relief="solid")
box2 = tk.Frame(catchildframe, borderwidth=2, relief="solid")
box3 = tk.Frame(catchildframe, borderwidth=2, relief="solid")
box1.grid(row=1, column=0, sticky='nsew', padx=10, pady=10)
box2.grid(row=1, column=1, sticky='nsew', padx=10, pady=10)
box3.grid(row=1, column=2, sticky='nsew', padx=10, pady=10)
box1.propagate(1)
box2.propagate(1)
box3.propagate(1)
rownum = rownum +1
def openFile():
global fname
global mindatetime
global maxdatetime
parentname = catmainframe.winfo_parent()
parent = catmainframe._nametowidget(parentname)
#childname = catchildframe.winfo_parent()
#child = catchildframe._nametowidget(childname)
child = add_file.master
print("Catmainframe parent:"+parentname)
#print("Catchildframe parent:"+child)
file_path=fd.askopenfilename()
#print(file_path)
file_name = os.path.basename(file_path)
print(file_name)
file_list = []
file_list.append(file_name)
df = pd.read_csv(file_path)
names = list(df.columns[0:])
indexcol = names[0]
#print(indexcol)
df = df.rename(columns = {indexcol:'datetime'})
names = list(df.columns[1:])
#print(names)
df.datetime = pd.to_datetime(df.datetime)
df.set_index('datetime',inplace=True)
if mindatetime == pd.to_datetime('1900-01-01 00:00:00'):
mindatetime = df.index.min()
elif mindatetime > df.index.min():
mindatetime = df.index.min()
if maxdatetime == pd.to_datetime('1900-01-01 00:00:00'):
maxdatetime = df.index.max()
elif maxdatetime < df.index.max():
maxdatetime = df.index.max()
print(mindatetime)
print(maxdatetime)
global unique_dates
unique_dates = []
unique_dates = df.index.map(pd.Timestamp.date).unique()
for x in range(len(names)):
if(len(names)==1):
l = tk.Checkbutton(box1, text=names[x], variable=names[x],state='disabled')
l.select()
l.pack(anchor = 'w')
else:
l = tk.Checkbutton(box1, text=names[x], variable=names[x])
l.select()
l.pack(anchor = 'w')
figure = plt.Figure(figsize=(4,3), dpi=100)
ax2 = figure.add_subplot(111)
line = FigureCanvasTkAgg(figure, box2)
line.get_tk_widget().grid(row=1,column=1,sticky='nsew')
df.plot(kind='line', legend=False, ax=ax2, fontsize=10)
ax2.set_title(cat)
ax2.set_xlim(mindatetime,maxdatetime)
for x in range(len(unique_dates)):
d = tk.Checkbutton(box3, text=unique_dates[x], variable=unique_dates[x])
d.select()
d.pack(anchor = 'w')
root = tk.Tk()
root.geometry('{}x{}'.format(800, 600))
# layout all of the main containers
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)
#Global variables
category = tk.StringVar()
global rownum
rownum =0
mindatetime = pd.to_datetime('1900-01-01 00:00:00')
maxdatetime = pd.to_datetime('1900-01-01 00:00:00')
#Top frame
top_frame = tk.Frame(root)
top_frame.grid(row=0, column=0, sticky='nsew')
category_name = tk.Label(top_frame, text='Category:')
category_name.grid(row=0, column=0, sticky='nsew')
entry_category = tk.Entry(top_frame, background="pink",textvariable = category)
entry_category.grid(row=0, column=1, sticky='nsew')
entry_category.focus()
ok_button = tk.Button(top_frame, text="OK", command=enterCategory)
ok_button.grid(row=0, column=2, sticky='nsew')
xscrollbar = tk.Scrollbar(root, orient='horizontal')
xscrollbar.grid(row=3, column=0, sticky='ew')
yscrollbar = tk.Scrollbar(root)
yscrollbar.grid(row=1, column=1, sticky='ns')
canvas1 = tk.Canvas(root, bd=0,#scrollregion=(0, 0, 1000, 1000),
yscrollcommand=yscrollbar.set)
canvas1.grid(row=1, column=0, sticky='nsew')
# create the center widgets
canvas1.grid_rowconfigure(1, weight=1)
canvas1.grid_columnconfigure(0, weight=1)
canvas2 = tk.Canvas(root, bd=0,#scrollregion=(0, 0, 1000, 1000),
xscrollcommand=xscrollbar.set)
canvas2.grid(row=2, column=0, sticky='nsew')
xscrollbar.config(command=canvas2.xview)
yscrollbar.config(command=canvas1.yview)
canvas1.config(scrollregion=canvas1.bbox("all"))
canvas2.config(scrollregion=canvas2.bbox("all"))
main_frame = tk.Frame(canvas1)
canvas1.create_window((0,0), window=main_frame,anchor='nw')
#main_frame.grid(row=0,column=0,stick='nsew')
time_box = tk.Frame(canvas2)
canvas2.create_window((0,0), window=time_box,anchor='nw')
root.bind('<Configure>', on_configure)
root.mainloop()
Below is the error even though the application seems to run fine.
Traceback (most recent call last):
File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 3069, in set
self.tk.call(self._w, 'set', first, last)
_tkinter.TclError: invalid command name ".!scrollbar2"
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "C:\Users\ranji\Anaconda3\lib\tkinter\__init__.py", line 3069, in set
self.tk.call(self._w, 'set', first, last)
_tkinter.TclError: invalid command name ".!scrollbar"
You have to manually manage the size of an inner frame yourself. You can do this by explicitly setting the width of the inner window to be the width of the canvas (minus any desired borders or margins) whenever the canvas changes size. You can get a callback when the window changes size by binding to the <Configure> event.
I've pared your code down to just the issue being asked about. Notice in the following code that I added a tag to the embedded window object so that it can be referenced in the on_configure function. I've also colorized the inner frame to make it easier to visualize, and given it a height since there are no widgets inside.
The important part of this example is the call to canvas1.itemconfigure in on_configure:
import tkinter as tk
def on_configure(event):
width = canvas1.winfo_width()
canvas1.itemconfigure("main_frame", width=width)
root = tk.Tk()
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)
yscrollbar = tk.Scrollbar(root)
canvas1 = tk.Canvas(root, bd=0,yscrollcommand=yscrollbar.set)
canvas1.grid(row=1, column=0, sticky='nsew')
yscrollbar.grid(row=1, column=1, sticky='ns')
main_frame = tk.Frame(canvas1, background="bisque", height=200)
canvas1.create_window((0,0), window=main_frame,anchor='nw', tags=("main_frame",))
root.bind('<Configure>', on_configure)
root.mainloop()
Recently, I tried to make a full application window with a side panel menu with separate frames running some functions and submitting forms in the canvas frame.
But I found that every time I click on any entry in listbox it runs the function or method without clearing the existing one .
I tried destroy() and forget() didn't work for me (maybe I didn't know exactly how to use it?!, and the destroy() function prevent using the function again till I close the whole application and run it again!) this is a photo of my problem
this is my code :
import tkinter as tk
from tkinter import ttk
class MainWindow() :
def __init__(self,root):
# menu left
self.menu_upper_frame = tk.Frame(root, bg="#dfdfdf")
self.menu_title_label = tk.Label(self.menu_upper_frame, text="menu title", bg="#dfdfdf")
self.menu_title_label.pack()
self.menu_left_container = tk.Frame(root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left_container, width=150, height=150, bg="red")
self.menu_left_upper.pack(side="top", fill="both", expand=True)
# create a listbox of items
self.Lb1 = tk.Listbox(self.menu_left_upper,bg ="red", borderwidth=0, highlightthickness=0 )
self.Lb1.insert(1, "Python")
self.Lb1.insert(2, "Perl")
self.Lb1.insert(3, "C")
self.Lb1.insert(4, "PHP")
self.Lb1.insert(5, "JSP")
self.Lb1.insert(6, "Ruby")
self.Lb1.bind("<<ListboxSelect>>", self.OnClick ) #return selected item
self.Lb1.pack(fill="both", expand=True, pady=50 )
# right area
self.inner_title_frame = tk.Frame(root, bg="#dfdfdf")
self.inner_title_label = tk.Label(self.inner_title_frame, text="inner title", bg="#dfdfdf")
self.inner_title_label.pack()
self.canvas_area = tk.Canvas(root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_upper_frame.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.menu_left_container.grid(row=1, column=0, rowspan=2, sticky="nsew")
self.inner_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(1, weight=1)
def OnClick(self,event):
widget = event.widget
selection = widget.curselection()
value = widget.get(selection)
if value == 'Python':
self.tabtop()
def tabtop(self):
self.tabControl = ttk.Notebook(self.canvas_area, width=400) # Create Tab Control
self.tab1 = ttk.Frame(self.tabControl) # Create a tab
self.tab2 = ttk.Frame(self.tabControl)
self.tab3 = ttk.Frame(self.tabControl)
self.tab4 = ttk.Frame(self.tabControl)
self.tab5 = ttk.Frame(self.tabControl)
self.tabControl.add(self.tab1, text='Login data' ) # Add the tab
self.tabControl.add(self.tab2, text='Permission')
self.tabControl.add(self.tab3, text='Roles')
self.tabControl.add(self.tab4, text='Personal data')
self.tabControl.add(self.tab5, text='Business data')
self.tabControl.pack(expand=1, fill="both") # Pack to make visible
self.l2 = tk.Label(self.tab2, text="label 2").pack()
self.l3 = tk.Label(self.tab3, text="label 3").pack()
root = tk.Tk()
root.title("Control Panel")
root.style = ttk.Style()
root.style.theme_use("clam")
user = MainWindow(root)
root.mainloop()
If what you're really asking is how to replace an existing notebook with a new notebook, all you need to do is call destroy() on the old notebook before creating the new one.
First, define self.tabControl to None somewhere in MainWindow.__init__. Then, in tabtop you can delete the old notebook before creating the new one:
def tabtop(self):
if self.tabControl is not None:
self.tabControl.destroy()
...
Based on the example from here: Scrollbar in Tkinter grid
I made a simplified version which is more or less what i need except i would like the columns to always fill the width of the frame as the window is beeing resized.
Without the scrollbar it was super easy, i just added grid_columnconfigure and that worked out of the box, but when I added the scrollbar i couldn't figure out how to get the columns to resize again.
Here is the example:
import tkinter as tk
row = 1
class ProgramWindow(tk.Frame):
def __init__(self):
self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
tk.Frame.__init__(self, self.canvas)
self.grid(column=0, row=0, sticky='ESW')
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
self.grid_columnconfigure(2, weight=1)
tk.Label(self, text="FirstCol", ).grid(row=0, column=0)
tk.Label(self, text="SecndCol", ).grid(row=0, column=1)
tk.Label(self, text="ThirdCol", ).grid(row=0, column=3)
self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4, 4), window=self)
self.bind("<Configure>", self.OnFrameConfigure)
def OnFrameConfigure(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def addrow(self, stuff, otherstuff):
global row
var = tk.StringVar(value=stuff)
entry = tk.Entry(self, textvariable=var)
entry.grid(row=row, column=0)
var = tk.StringVar(value=otherstuff)
entry = tk.Entry(self, textvariable=var)
entry.grid(row=row, column=1)
var = tk.StringVar(value="foobar")
entry = tk.Entry(self, textvariable=var)
entry.grid(row=row, column=3)
row += 1
def SomeProg():
for i in range(20):
stuff = "Stuff is " + str(i)
otherstuff = i * 4
win.addrow(stuff, otherstuff)
root = tk.Tk()
root.title("Stuff")
win = ProgramWindow()
SomeProg()
root.mainloop()
I've adapted Bryan Oakley's answer to Adding a scrollbar to a group of widgets in Tkinter so that the frame contained in the canvas fit the width of the canvas.
Canvas window objects have a width option. So, each time the canvas is resized, I pass the new canvas width to this width option using
self.canvas.itemconfigure(<frame tag>, width=self.canvas.winfo_width())
Here is the full code
import tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
self.frame = tk.Frame(self.canvas, background="#ffffff")
self.frame.columnconfigure(0, weight=1)
self.frame.columnconfigure(1, weight=1)
self.frame.columnconfigure(2, weight=1)
self.vsb = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((4,4), window=self.frame, anchor="nw",
tags="self.frame")
self.canvas.bind("<Configure>", self.onCanvasConfigure)
self.populate()
def populate(self):
'''Put in some fake data'''
for i in range(100):
tk.Entry(self.frame).grid(row=i, column=0, sticky='ew')
tk.Entry(self.frame).grid(row=i, column=1, sticky='ew')
tk.Entry(self.frame).grid(row=i, column=2, sticky='ew')
def onCanvasConfigure(self, event):
self.canvas.itemconfigure("self.frame", width=self.canvas.winfo_width())
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
if __name__ == "__main__":
root=tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()