How can I get the scrollbar to show up on canvas.
# python dashboard
import tkinter as tk
from tkinter import *
#======================{{{
class AutoScrollbar(Scrollbar):
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
#====}}}
#===================={{{
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
canvasContainer = tk.Frame(root)
self.canvas=tk.Canvas(canvasContainer, width=1000, height=700,background='#002B36')
canvasContainer.grid(row=0,column=3)
frame = Frame(self.root, bd=2, relief=SUNKEN)
frame.grid(row=0,column=0, sticky="nw")
b1=Button(frame,text="Status", command=lambda color="#DC322F", filename="dashboard_content.txt" : self.contents(color,filename)).grid(row = 0,column = 0, sticky = "we")
b2=Button(frame,text="Processes", command=lambda color="#859900", filename="process.log" : self.contents(color,filename)).grid(row = 0,column = 1, sticky = "we")
b3=Button(frame,text="Links", command=lambda color="#B58900", filename="links.log" : self.contents(color,filename)).grid(row = 1,column = 0, sticky = "we")
b4=Button(frame,text="Traffic", command=lambda color="#268BD2", filename="version.log" : self.contents(color,filename)).grid(row = 1,column = 1, sticky = "we")
b5=Button(frame,text="App Version", command=lambda color="#D33682", filename="version.log" : self.contents(color,filename)).grid(row = 2,column = 0, sticky = "we")
b6=Button(frame,text="Archive/Purge", command=lambda color="#93A1A1", filename="cleanup.log" : self.contents(color,filename)).grid(row = 2,column = 1, sticky = "we")
# self.contents("blue","dashboard_content.txt")
# b1.bind("<ButtonPress-1>", lambda events, color="blue", filename="dashboard_content.txt" : self.contents(color,filename))
vsb = AutoScrollbar(canvasContainer,orient=VERTICAL)
hsb = AutoScrollbar(canvasContainer, orient=HORIZONTAL)
vsb.grid(row=0, column=2, sticky="ns")
hsb.grid(row=1, column=1, sticky="ew")
self.canvas.grid(row=0,column=1,sticky="news")
self.canvas.config(yscrollcommand=vsb.set, xscrollcommand=hsb.set, scrollregion=self.canvas.bbox("all"))
vsb.config(command=self.canvas.yview)
hsb.config(command=self.canvas.xview)
canvasContainer.grid_rowconfigure(0, weight=1)
canvasContainer.grid_columnconfigure(0, weight=1)
self.canvas.update_idletasks()
# Grid.columnconfigure(self.root,1,weight=1, minsize=100)
def contents(self, color="blue", filename="dashboard_content.txt"):
fhandle = open(filename)
lines = fhandle.read()
fhandle.close()
self.canvas.delete("all")
text1=self.canvas.create_text(0, 0, fill=color, anchor=NW)
self.canvas.itemconfig(text1, text=lines)
#===========}}}
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
If I don't have the contents function, the scrollbar appears. How Can I have a scrollbar around canvas in this case.
You have several problems in your code. The biggest problem is that you are trying to solve too many problems at once.
If you are just starting out, you need to solve layout problems one at a time. It appears you want two major areas in your GUI -- a panel of buttons, and a canvas with scrollbars. So, start by creating two frames, one for each. Give them distinctive colors so you can see what is what. Make sure this two areas are fully functional before trying anything else By that I mean, make sure they resize appropriately when you resize the window.
So, start with the following simple program. Notice that there are no buttons and no canvas. We're just creating the basic scaffolding of the GUI.
import Tkinter as tk
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
# the width, height and colors are temporary,
# until we have more of the GUI working.
buttonPanel = tk.Frame(root, background="green", width=200, height=200)
canvasPanel = tk.Frame(root, background="pink", width=500, height=500)
# because these two panels are side-by-side, pack is the
# best choice:
buttonPanel.pack(side="left", fill="y")
canvasPanel.pack(side="right", fill="both", expand=True)
# fill in these two areas:
self._create_buttons(buttonPanel)
self._create_canvas(canvasPanel)
def _create_buttons(self, parent):
pass
def _create_canvas(self, parent):
pass
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
Does that behave the way you expect? I'll assume so. Now, as long as the buttons go in the left, and the canvas and scrollbars go on the right, we no longer have to worry about the two areas interacting with each other.
Now, create the canvas:
def _create_canvas(self, parent):
self.canvas=tk.Canvas(parent, width=1000, height=700,background='#002B36')
vsb = tk.Scrollbar(parent, command=self.canvas.yview, orient="vertical")
hsb = tk.Scrollbar(parent, command=self.canvas.xview, orient="horizontal")
self.canvas.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
vsb.grid(row=0, column=0, sticky="ns")
hsb.grid(row=1, column=0, sticky="ew")
self.canvas.grid(row=0, column=1, sticky="nsew")
parent.grid_rowconfigure(0, weight=1)
parent.grid_columnconfigure(1, weight=1)
Run the program again. Do you see the scrollbars? Is everything still working properly when you resize the window?
The next step would be to add the buttons:
def _create_buttons(self, parent):
b1=tk.Button(parent,text="Status", command=lambda: self._contents("#DC322F", "dashboard_content.txt"))
b2=tk.Button(parent,text="Processes", command=lambda: self._contents("#859900", "process.log"))
b3=tk.Button(parent,text="Links", command=lambda: self._contents("#B58900", "links.log"))
b4=tk.Button(parent,text="Traffic", command=lambda: self._contents("#268BD2", "version.log"))
b5=tk.Button(parent,text="App Version", command=lambda: self._contents("#D33682", "version.log"))
b6=tk.Button(parent,text="Archive/Purge", command=lambda: self._contents("#93A1A1", "cleanup.log"))
b1.grid(row = 0,column = 0, sticky = "we")
b2.grid(row = 0,column = 1, sticky = "we")
b3.grid(row = 1,column = 0, sticky = "we")
b4.grid(row = 1,column = 1, sticky = "we")
b5.grid(row = 2,column = 0, sticky = "we")
b6.grid(row = 2,column = 1, sticky = "we")
Run the program again, and make sure there are still no layout problems.
See where this is going? Solve just one problem at a time. Organize your code in sections. Collect all of your layout commands in one place so you can better visualize what widgets go together.
Related
I'm new to Tkinter, and have a problem with my frames when adding widgets. In this example, I add a button which makes my frame wider when I place the button inside it with .grid().
How can I make the frame "fixed"? I want the blue frame in the code below to keep the same width when I add the button.
Thanks in advance.
Regards,
Laphroaig
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
master.title("Yatzy - The Game")
master.geometry("800x600+0+0")
master.iconbitmap(r'dice.ico')
master.state('zoomed')
# Create grid index for the window
for r in range(20):
self.master.rowconfigure(r, weight=1)
for c in range(20):
self.master.columnconfigure(c, weight=1)
# Place Frame 1
Frame1 = Frame(master, bg="blue")
Frame1.grid(row = 0, column = 0, rowspan = 20, columnspan = 3, sticky=W+E+N+S)
# Place Frame 2
Frame2 = Frame(master, bg="green")
Frame2.grid(row=0, column=3, rowspan=20, columnspan=17, sticky = W+E+N+S)
# Place Frame 3
Frame3 = Frame(master, bg="red")
Frame3.grid(row=5, column=8, rowspan=10, columnspan=7, sticky = W+E+N+S)
# Place button 1
btn_1 = Button(master, text="hello123")
btn_1.grid(row=0, column=0)
root = Tk()
app = Window(master=root)
app.mainloop()
You can stop content from affecting the size of a Frame with grid_propagate(False). See example below.
Other things; You inherit from Frame but never put anything inside self, instead you put everything inside self.master ie. root. I changed to put everything in self and then pack self within root.
I also removed the icon as I don't have your icon file.
from tkinter import *
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
master.title("Yatzy - The Game")
master.geometry("800x600+0+0")
master.state('zoomed')
self.pack(expand=True, fill='both')
# Create grid index for the window
for r in range(20):
self.rowconfigure(r, weight=1)
for c in range(20):
self.columnconfigure(c, weight=1)
# Place Frame 1
Frame1 = Frame(self, bg="blue")
Frame1.grid(row = 0, column = 0, rowspan = 20, columnspan = 3, sticky=W+E+N+S)
Frame1.grid_propagate(False) # Stop grid() from resizing container
# Place Frame 2
Frame2 = Frame(self, bg="green")
Frame2.grid(row=0, column=3, rowspan=20, columnspan=17, sticky = W+E+N+S)
# Place Frame 3
Frame3 = Frame(self, bg="red")
Frame3.grid(row=5, column=8, rowspan=10, columnspan=7, sticky = W+E+N+S)
# Place button 1
btn_1 = Button(Frame1, text="hello123")
btn_1.grid(row=0, column=0)
root = Tk()
app = Window(master=root)
app.mainloop()
I've created a simple GUI application with Tkinter.
I have two questions about this code:
import tkinter as tk
from tkinter import ttk, Grid, Frame, N, S, W, E, StringVar, Label, Entry, RAISED, Button, Checkbutton, Scrollbar
class mainApp(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.scan_button = Button(self.parent, text="Scan", command=self.scan_wifi)
self.forget_button = Button(self.parent, text="Forget", command=self.forget_wifi)
self.reboot_button = Button(self.parent, text="Reboot", command=self.reboot)
frame=Frame(self.parent)
Grid.rowconfigure(self.parent, 0, weight=1)
Grid.columnconfigure(self.parent, 0, weight=1)
frame.grid(row=0, column=0, sticky=N+S+E+W)
grid=Frame(self.parent)
grid.grid(sticky=N+S+E+W, column=0, row=7, columnspan=2)
Grid.rowconfigure(self.parent, 7, weight=1)
Grid.columnconfigure(self.parent, 0, weight=1)
headings=('Name', 'Address', 'Quality', 'Channel', 'Signal Level', 'Encryption')
row=[]
rows=[]
self.table = ttk.Treeview(show="headings", selectmode="browse")
self.table["columns"]=headings
self.table["displaycolumns"]=headings
for head in headings:
self.table.heading(head, text=head, anchor=tk.CENTER)
self.table.column(head, width=30, anchor=tk.CENTER)
self.scrolltable = tk.Scrollbar(command=self.table.yview)
self.table.configure(yscrollcommand=self.scrolltable.set)
self.scrolltable.grid(row=1, column=100, sticky=N+S)
for row in rows:
self.table.insert('', tk.END, values=tuple(row))
self.table.bind('<ButtonRelease-1>', self.OnRelease)
self.table.grid(row=0, rowspan=14, columnspan = 21, sticky=N+S+E+W)
self.scan_button.grid(row=15, column = 1, columnspan = 1, sticky=N+S+E+W)
self.forget_button.grid(row=15, column = 0, columnspan = 1 , sticky=N+S+E+W)
self.reboot_button.grid(row=15, column = 3, columnspan = 1 , sticky=N+S+E+W)
def OnRelease(self, event):
pass
def scan_wifi(self):
pass
def forget_wifi(self):
pass
def reboot(self):
pass
root=tk.Tk()
app = mainApp(root)
root.mainloop()
1) How I can move the button on the top of the window?
2) Why, if I resize the window, the button "Forget" becomes bigger than other buttons? How I can make all buttons identical size?
Because you have called columnconfigure(0, weight=1) only, therefore only the Forget button will be resized when the window is resized.
To move the buttons to the top of the window, you need to rearrange the buttons to row=0 and the Treeview to row=1. Below is modified code based on yours:
import tkinter as tk
from tkinter import ttk
class mainApp(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
# buttons
self.forget_button = tk.Button(self.parent, text='Forget', command=self.forget_wifi)
self.scan_button = tk.Button(self.parent, text='Scan', command=self.scan_wifi)
self.reboot_button = tk.Button(self.parent, text='Reboot', command=self.reboot)
self.forget_button.grid(row=0, column=0, sticky='nsew')
self.scan_button.grid(row=0, column=1, sticky='nsew')
self.reboot_button.grid(row=0, column=2, sticky='nsew')
# make the 3 buttons same width
for i in range(3):
self.parent.columnconfigure(i, weight=1)
# make the treeview and the scrollbar to fill the remaining space
self.parent.rowconfigure(1, weight=1)
# treeview and scrollbar
frame = tk.Frame(self.parent)
frame.grid(row=1, column=0, columnspan=3, sticky='nsew')
headings=('Name', 'Address', 'Quality', 'Channel', 'Signal Level', 'Encryption')
self.table = ttk.Treeview(frame, show='headings', selectmode='browse')
self.table['columns'] = headings
self.table['displaycolumns'] = headings
for head in headings:
self.table.heading(head, text=head, anchor=tk.CENTER)
self.table.column(head, width=30, anchor=tk.CENTER)
self.scrolltable = tk.Scrollbar(frame, command=self.table.yview)
self.table.configure(yscrollcommand=self.scrolltable.set)
self.table.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
self.scrolltable.pack(side=tk.RIGHT, fill=tk.Y)
self.table.bind('<ButtonRelease-1>', self.OnRelease)
def OnRelease(self, event):
pass
def scan_wifi(self):
pass
def forget_wifi(self):
pass
def reboot(self):
pass
root=tk.Tk()
app = mainApp(root)
root.mainloop()
I have written code for a small python tkinter application which goes as follows
import tkinter as tk
from tkinter import *
import tkinter.filedialog as fdialog
class karl(Frame):
def __init__(self):
tk.Frame.__init__(self)
self.pack(fill = tk.BOTH)
self.master.title("Image Selector")
self.master.geometry("500x500")
self.master.resizable(0, 0)
self.pack_propagate(0)
self.label_button_1 = Label(self, text="Select directory for picking images")
self.label_button_1.grid(row = 0, column = 1, rowspan = 1, columnspan = 2, sticky = W)
self.button_1 = tk.Button(self, text="CLICK HERE", width=25, command=self.open_dialog_box_to_select_folder)
self.button_1.grid(row=0, column=20, rowspan=1, columnspan=2, sticky=E)
self.label_for_label_directory = Label(self, text="Current chosen directory")
self.label_for_label_directory.grid(row=20, column=1, rowspan=1, columnspan=2, sticky=E)
self.label_directory = Label(self, text="")
self.label_directory.grid(row=20, column=5, rowspan=1, columnspan=2, sticky=W)
self.label_for_entry_for_class_label_values = Label(self, text="Enter text")
self.label_for_entry_for_class_label_values.grid(row = 24, column = 1, rowspan = 1, columnspan = 2, sticky = W)
self.entry_for_class_label_values = Entry(self)
self.entry_for_class_label_values.grid(row = 24, column = 5, rowspan = 1, columnspan = 2, sticky = E)
def open_dialog_box_to_select_folder(self):
self.chosen_directory_name = fdialog.askdirectory()
self.label_directory.config(text = self.chosen_directory_name)
def main():
karl().mainloop()
if __name__ == '__main__':
main()
As soon as button_1 is clicked and the label_directory gets updated with the string for directory, the position of button_1 gets pushed towards the right and goes outside the application window. How can I stop this from happening?
The problem is that when you are selecting a folder and updating self.label_directory with the String of the path to the selected folder it is manipulating the grid and expanding your Click Here Button.
To fix this issue, firstly button_1 should have a its sticky option set to W, so then it doesn't move to the right side of it's grid cell, when it's grid cells width is manipulated.
Another issue you have is that you are increasing you rows and columns of your grid placement too much, you should only increment by the needed spaces, for example, you should start at row 1 and then place your next row at row 2, this helps make sure it is easy to understand where each element is placed.
With that all said I believe this code will fix the issue suitably:
import tkinter as tk
from tkinter import *
import tkinter.filedialog as fdialog
class karl(Frame):
def __init__(self):
tk.Frame.__init__(self)
self.pack(fill = tk.BOTH)
self.master.title("Image Selector")
self.master.geometry("500x500")
self.master.resizable(0, 0)
self.pack_propagate(0)
self.label_button_1 = Label(self, text="Select directory for picking images")
self.label_button_1.grid(row = 0, column = 0, columnspan = 1, sticky = W)
self.button_1 = tk.Button(self, text="CLICK HERE", width=25, command=self.open_dialog_box_to_select_folder)
self.button_1.grid(row=0, column=1, columnspan=2, sticky=W)
self.label_for_label_directory = Label(self, text="Current chosen directory")
self.label_for_label_directory.grid(row=1, column=0, rowspan=1, columnspan=1, sticky=W)
self.label_directory = Label(self, text="")
self.label_directory.grid(row=1, column=1, rowspan=1, columnspan=2, sticky=W)
self.label_for_entry_for_class_label_values = Label(self, text="Enter (+) seperated class labels\nto be assigned to the images")
self.label_for_entry_for_class_label_values.grid(row = 2, column = 0, rowspan = 1, columnspan = 2, sticky = W)
self.entry_for_class_label_values = Entry(self)
self.entry_for_class_label_values.grid(row = 2, column = 1, rowspan = 1, columnspan = 1, sticky = W)
def open_dialog_box_to_select_folder(self):
self.chosen_directory_name = fdialog.askdirectory()
self.label_directory.config(text = self.chosen_directory_name)
def main():
karl().mainloop()
if __name__ == '__main__':
main()
I changed the elements previously mentioned to accomplish this, if you have any questions about my solution feel free to ask :)
Here is your tkinter program converted to an excel file, to visualize how the grid system works, the light blue is where your path string will be put when you select a folder.
When you select a long file path, the path_label expands (the light blue section of the excel, this will push the click me button to the right. So far right that it will push the button of the windows view-able area like so.
I'm trying to build a dashboard.
I want to keep the scrollbar just for the canvas area, but, on the contrary, it just stick to other frame.
The code I have written is this
# python dashboard
import tkinter as tk
from tkinter import *
class AutoScrollbar(Scrollbar):
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
vsb = AutoScrollbar(root)
vsb.grid(row=0, column=1, sticky=N+S)
hsb = AutoScrollbar(root, orient=HORIZONTAL)
hsb.grid(row=1, column=0, sticky=E+W)
self.canvas=tk.Canvas(root,yscrollcommand=vsb.set,
xscrollcommand=hsb.set,background='blue')
vsb.config(command=self.canvas.yview)
hsb.config(command=self.canvas.xview)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
self.canvas.grid(row=0,column=3)
frame = Frame(self.root, bd=2, relief=SUNKEN)
frame.grid(row=0,column=0, sticky="nw")
Button1=Button(frame,text="Status").grid(row = 0,column = 0, sticky = "we")
Button2=Button(frame,text="Processes").grid(row = 0,column = 1, sticky = "we")
Button3=Button(frame,text="Links").grid(row = 1,column = 0, sticky = "we")
Button4=Button(frame,text="Traffic").grid(row = 1,column = 1, sticky = "we")
Button5=Button(frame,text="App Version").grid(row = 2,column = 0, sticky = "we")
Button5=Button(frame,text="Archive/Purge").grid(row = 2,column = 1, sticky = "we")
fhandle = open("dashboard_content.txt")
lines = fhandle.read()
fhandle.close()
text1=self.canvas.create_text(400, 400, fill="white")
self.canvas.itemconfig(text1, text=lines)
frame.update_idletasks()
self.canvas.config(scrollregion=self.canvas.bbox("all"))
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
How can I get the scrollbar just for the Canvas window, "to the blue background"?
I'm guessing you mean you want to leave all of the blank space surrounding the canvas, but you want the scrollbar to "stick" to the side of the canvas.
The easiest way to accomplish that is to put both the canvas and the scrollbars in a frame so that they act as a single unit.
For example:
class Dashboard():
def __init__(self, root):
....
canvasContainer = tk.Frame(root)
self.canvas = tk.Canvas(canvasContainer, ...)
vsb = AutoScrollbar(canvasContainer, ...)
hsb = AutoScrollbar(canvasContainer, ...)
vsb.grid(row=0, column=0, sticky="ns")
hsb.grid(row=1, column=1, sticky="ew")
self.canvas.grid(row=0, column=1, sticky="nsew")
canvasContainer.grid_rowconfigure(0, weight=1)
canvasContainer.grid_columnconfigure(1, weight=0)
...
canvasContainer.grid(row=0, column=3)
...
#Bryan , Well all I wanted was to stick the scrollbar to the canvas area only and a sidebar on the left with buttons. With a bit modification to your suggestion I could manage to achieve. Here is what I did.
# python dashboard
import tkinter as tk
from tkinter import *
class AutoScrollbar(Scrollbar):
def set(self, lo, hi):
if float(lo) <= 0.0 and float(hi) >= 1.0:
self.tk.call("grid", "remove", self)
else:
self.grid()
Scrollbar.set(self, lo, hi)
def pack(self, **kw):
raise TclError("cannot use pack with this widget")
def place(self, **kw):
raise TclError("cannot use place with this widget")
class Dashboard():
def __init__(self, root):
self.root=root
root.title("Dashboard View")
canvasContainer = tk.Frame(root)
self.canvas=tk.Canvas(canvasContainer,background='blue')
fhandle = open("dashboard_content.txt")
lines = fhandle.read()
fhandle.close()
text1=self.canvas.create_text(400, 400, fill="white")
self.canvas.itemconfig(text1, text=lines)
vsb = AutoScrollbar(canvasContainer,orient=VERTICAL)
hsb = AutoScrollbar(canvasContainer, orient=HORIZONTAL)
vsb.grid(row=0, column=2, sticky="ns")
hsb.grid(row=1, column=1, sticky="ew")
self.canvas.grid(row=0,column=1,sticky="news")
self.canvas.config(yscrollcommand=vsb.set, xscrollcommand=hsb.set, scrollregion=self.canvas.bbox("all"))
vsb.config(command=self.canvas.yview)
hsb.config(command=self.canvas.xview)
canvasContainer.grid_rowconfigure(0, weight=1)
canvasContainer.grid_columnconfigure(0, weight=1)
canvasContainer.grid(row=0,column=3)
frame = Frame(self.root, bd=2, relief=SUNKEN)
frame.grid(row=0,column=0, sticky="nw")
Button1=Button(frame,text="Status").grid(row = 0,column = 0, sticky = "we")
Button2=Button(frame,text="Processes").grid(row = 0,column = 1, sticky = "we")
Button3=Button(frame,text="Links").grid(row = 1,column = 0, sticky = "we")
Button4=Button(frame,text="Traffic").grid(row = 1,column = 1, sticky = "we")
Button5=Button(frame,text="App Version").grid(row = 2,column = 0, sticky = "we")
Button5=Button(frame,text="Archive/Purge").grid(row = 2,column = 1, sticky = "we")
self.canvas.update_idletasks()
# Grid.columnconfigure(self.root,1,weight=1, minsize=100)
if __name__== '__main__':
root=tk.Tk()
board=Dashboard(root)
root.mainloop()
Any idea, How can I make the complete window expandable.
def create_layout(frame):
frame = Frame(frame, bg = 'red')
frame.pack(side = LEFT, fill=BOTH)
b = Button(frame, text='Button1', command=pressed, padx = 20)
b.pack(pady = 20, padx = 20)
c = Button(frame, text='Button2', command=pressed, padx=20)
c.pack(pady = 20, padx = 20)
I got this code so far, assume that from Tkinter import * has already been called and the frame has already had its size and colour set. It should look like the picture below. However i can't ever get button 3 and 4 to the frame on the right, whenever i add a button it goes in the red frame.
OK, the first set of buttons, button 1 & 2 are in the "frame", buttons 3 & 4 should be left out.
So with buttons 1 & 2, open the frame with the bg of red, pack it with side=tk.LEFT, fill with both & expand it.
With buttons 3 & 4, just side them LEFT and expand. That will work like a treat ;-)
You need to add another frame that sits to the right, and then pack button3 and button4 into that. Maybe change the previous frame you have there to frame1 and then have:
frame2 = Frame(frame, bg = "yellow")
frame2.pack(side = RIGHT, fill = BOTH)
Then, create the buttons and pack them in. Hope this helps!
You have 2 frames, and 4 buttons.
Let us create a function called create_widgets() which will only consist in calling 2 other functions create_frames() and create_buttons()
For the frames, we use the grid() layout manager:
def create_frames(self):
self.left_frame = tk.Frame(width=140, height=140, background='red')
self.left_frame.grid(row=0, column=0)
self.right_frame = tk.Frame(width=300, height=140, background='gold2')
self.right_frame.grid(row=0, column=1)
This will create this interface:
Let us design create_buttons() in a way it only consists in calling to 2 different functions, each having a specific task:
create_left_frame_buttons() to create buttons for the left frame
create_right_frame_buttons() to create buttons for the right frame
Here is their simple implementation:
def create_buttons(self):
self.create_left_frame_buttons()
self.create_right_frame_buttons()
def create_left_frame_buttons(self):
self.button1 = tk.Button(self.left_frame, text='Button1')
self.button1.grid(row=0, column=0, padx=30, pady=20)
self.button2 = tk.Button(self.left_frame, text='Button2')
self.button2.grid(row=1, column=0, padx=30, pady=20)
def create_right_frame_buttons(self):
self.button1 = tk.Button(self.right_frame, text='Button3')
self.button1.grid(row=0, column=0, padx=20, pady=50)
self.button2 = tk.Button(self.right_frame, text='Button4')
self.button2.grid(row=0, column=1, padx=70)
Note that I used the options padx and pady to create a suitable spacing between the buttons.
Up to this moment, this is the resulting interface:
You can see both the left and right frames are shrinking, and the result is ugly. To fix this issue, we can set rid_propagate(0) for each frame.
So based on these observations and following Tkinter best practices, here is the full code:
import tkinter as tk
class MainApplication(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, self.master)
self.configure_gui()
self.create_widgets()
def configure_gui(self):
self.master.title('Simple layout')
self.master.geometry('440x140')
self.master.resizable(0, 0)
def create_widgets(self):
self.create_frames()
self.create_buttons()
def create_frames(self):
self.left_frame = tk.Frame(width=140, height=140, background='red')
self.left_frame.grid_propagate(0)
self.left_frame.grid(row=0, column=0)
self.right_frame = tk.Frame(width=300, height=140, background='gold2')
self.right_frame.grid_propagate(0)
self.right_frame.grid(row=0, column=1)
def create_buttons(self):
self.create_left_frame_buttons()
self.create_right_frame_buttons()
def create_left_frame_buttons(self):
self.button1 = tk.Button(self.left_frame, text='Button1')
self.button1.grid(row=0, column=0, padx=30, pady=20)
self.button2 = tk.Button(self.left_frame, text='Button2')
self.button2.grid(row=1, column=0, padx=30, pady=20)
def create_right_frame_buttons(self):
self.button1 = tk.Button(self.right_frame, text='Button3')
self.button1.grid(row=0, column=0, padx=20, pady=50)
self.button2 = tk.Button(self.right_frame, text='Button4')
self.button2.grid(row=0, column=1, padx=70)
if __name__ == '__main__':
root = tk.Tk()
main_app = MainApplication(root)
root.mainloop()
Demo: