Related
I'm using .place() in my tkinter program, so I wanted to remove references to grid(). So far my program works but for some reason there's a single .grid() line that makes my whole program turn blank if it's removed. This shouldn't happen, since I'm entirely using .place(). Here is that line:
AllFrames.grid(row=0, column=0, sticky='nsew')
And here is my full code:
from tkinter import *
root = Tk()
root.title("Account Signup")
DarkBlue = "#2460A7"
LightBlue = "#B3C7D6"
root.geometry('350x230')
Menu = Frame()
loginPage = Frame()
registerPage = Frame()
for AllFrames in (Menu, loginPage, registerPage):
AllFrames.grid(row=0, column=0, sticky='nsew')
AllFrames.configure(bg=LightBlue)
def show_frame(frame):
frame.tkraise()
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
show_frame(Menu)
# ============= Menu Page =========
menuTitle = Label(Menu, text="Menu", font=("Arial", 25), bg=LightBlue)
menuTitle.place(x=130, y=25)
loginButton1 = Button(Menu, width=25, text="Login", command=lambda: show_frame(loginPage))
loginButton1.place(x=85, y=85)
registerButton1 = Button(Menu, width=25, text="Register", command=lambda: show_frame(registerPage))
registerButton1.place(x=85, y=115)
# ======== Login Page ===========
loginUsernameL = Label(loginPage, text='Username').place(x=30, y=60)
loginUsernameE = Entry(loginPage).place(x=120, y=60)
loginPasswordL = Label(loginPage, text='Password').place(x=30, y=90)
loginPasswordE = Entry(loginPage).place(x=120, y=90)
backButton = Button(loginPage, text='Back', command=lambda: show_frame(Menu)).place(x=0, y=0)
loginButton = Button(loginPage, text='Login', width=20).place(x=100, y=150)
# ======== Register Page ===========
root.mainloop()
I've also noticed that changing anything in the parentheses causes the same result. For example, if I change sticky='nsew' to sticky='n' or row=0 to row=1 it will show a blank page.
How do I remove .grid() from my program without it turning blank?
The place() manager does not reserve any space, unless you tell it directly.
The grid(sticky='nsew') makes the widget expand to fill the entire available space, in this case the containing widget. The widgets inside all use place() which will not take any space. When you change to grid(sticky='n') you place the zero size widget at the top of the containing widget.
But, for your current problem you can assign a size to the widgets:
AllFrames.place(relwidth=1, relheight=1) # w/h relative to size of master
I would recommend using the grid() geometry manager if you are going to make any more complicated layouts.
For more info have a look at effbot on archive.org
import tkinter as tk
win = tk.Tk()
win.title('使用者登入')
win.geometry('300x200')
lb1 = tk.Label(win,text='使用者帳號資料',font=('微軟正黑體',16),fg='yellow',bg='black')
lb1.pack(fill='x')
lb2 = tk.Label(win,text='帳號 : ABCDEF',height=4,width=26,font=('標楷體',14),bg='lightblue')
lb2.pack(side='left',anchor='nw',fill='x')
lb3 = tk.Label(win,text='注意',height=50,width=3,font=('微軟正黑體',12),bg='pink')
lb3.pack(anchor='se',fill='y',expand=True)
lb4 = tk.Label(win,text='密碼 : 123456',height=4,width=15,font=('標楷體',14),bg='lightgreen')
lb4.pack(anchor='sw',side='left')
win.mainloop()
The variable lb4 could not be shown on the tkinter window even though I have used pack() method. I would like to put the lb4 widget below lb2 and I had tried using anchor='sw' still not showing so any possible methods to have that widget be shown properly?
The pack method works properly, check that:
import tkinter as tk
win = tk.Tk()
win.title('使用者登入')
win.geometry('300x200')
# lb1 = tk.Label(win,text='使用者帳號資料',font=('微軟正黑體',16),fg='yellow',bg='black')
# lb1.pack(fill='x')
#
# lb2 = tk.Label(win,text='帳號 : ABCDEF',height=4,width=26,font=('標楷體',14),bg='lightblue')
# lb2.pack(side='left',anchor='nw',fill='x')
#
# lb3 = tk.Label(win,text='注意',height=50,width=3,font=('微軟正黑體',12),bg='pink')
# lb3.pack(anchor='se',fill='y',expand=True)
lb4 = tk.Label(win,text='密碼 : 123456',height=4,width=15,font=('標楷體',14),bg='lightgreen')
lb4.pack(anchor='sw',side='left')
win.mainloop()
If it does show anything is because the pack method add widget to the right of the previous on if it's anchored to "S"(outh), even if the new widget it's anchored to 'w' and sided to 'left'.
Since lb3 is anchor at 'se', any other widget is displayed outside the windows.
You shoud use .grid to properly design your window:
import tkinter as tk
win = tk.Tk()
win.title('使用者登入')
lb1 = tk.Label(win, text='使用者帳號資料', font=('微軟正黑體', 16), fg='yellow',
bg='black')
lb1.grid(row=0, column=0, columnspan=3, sticky='WE')
lb2 = tk.Label(win, text='帳號 : ABCDEF', height=4, width=26, font=('標楷體', 14),
bg='lightblue')
lb2.grid(row=1, column=0, sticky='WE')
lb3 = tk.Label(win, text='注意', width=3, font=('微軟正黑體', 12), bg='pink')
lb3.grid(row=1, column=2, rowspan=2, sticky='NS')
#
lb4 = tk.Label(win, text='密碼 : 123456', height=4, width=15, font=('標楷體', 14),
bg='lightgreen')
lb4.grid(row=2, column=0, sticky='w')
win.mainloop()
It looks like this:
Here, I removed the height of lb3 since it's not required (sticky 'NS' allow us to extend the widget to top and down of the rowspan).
You can change column, row, columnspan, rowspan as you want to create the layout you desire.
Each time you use pack, it allocates an entire side for the widget. Thus, the order in which you call pack matters. For example, once you put widget along the top, you can no longer put something to the right.
This is usually much easier to visualize when you group all of your pack statements together for all children in the same parent widget. I also recommend always explicitly defining the side parameter so that your intentions are crystal clear.
This is the proper order to use pack to get lb4 below lb2, with lb1 along the top and lb3 along the right side:
lb1.pack(side="top", fill='x')
lb3.pack(side="right", fill='y')
lb2.pack(side="top", fill='x')
lb4.pack(side="top", fill='x')
I tried running your code and using lb4 before lb3 fixes the problem, but it won't display below lb2 as you have already used side='left' in lb2 so it acquires the whole left side, to pack it below lb2 remove the side='left' in lb2 and put it in lb4.
It doesn't display lb4 at first because it packs it outside of the TKinter window .
Instead of using .pack() use .grid() to better place the labels in the window.
This works:
import tkinter as tk
win = tk.Tk()
win.title('使用者登入')
win.geometry('300x200')
lb1 = tk.Label(win,text='使用者帳號資料',font=('微軟正黑體',16),fg='yellow',bg='black')
lb1.pack(fill='x')
lb2 = tk.Label(win,text='帳號 : ABCDEF',height=4,width=26,font=('標楷體',14),bg='lightblue')
lb2.pack(anchor='nw',fill='x')
lb4 = tk.Label(win,text='密碼 : 123456',height=4,width=15,font=('標楷體',14),bg='lightgreen')
lb4.pack(side='left',anchor='sw')
lb3 = tk.Label(win,text='注意',height=50,width=3,font=('微軟正黑體',12),bg='pink')
lb3.pack(anchor='se',fill='y',expand=True)
win.mainloop()
I'm playing around with the scrollbar and the ttk.Notebook functions to create two scrollable tabs. I have the mousewheel bound to scrolling, but it always scrolls both tabs. When you first run the code, you can scroll the first tab by itself because the second tab hasn't been "activated" for lack of a better term. Once you click the second tab to view it, all further scrolling on either tab scrolls both tabs evenly. The scrollbars do work independently when scrolled manually.
Is there a way to only bind the mousewheel to the selected tab at any one time?
Edit: I've noticed another weird behavior that if you resize the window outward everything reacts as it should, but if you resize the window making it narrower, the scrollbar disappears to the right of the now narrower window.
from tkinter import *
from tkinter import ttk
Window = Tk()
# Create the Outer Frame
outer_frame = Frame(Window)
outer_frame.pack(fill=BOTH, expand=1)
# Create and add the tabs
tab_control = ttk.Notebook(outer_frame)
tab1 = ttk.Frame(tab_control)
tab_control.add(tab1, text="Tab 1")
tab2 = ttk.Frame(tab_control)
tab_control.add(tab2, text="Tab 2")
tab_control.pack(expand=1, fill=BOTH)
# Create the canvases
canvas1 = Canvas(tab1)
canvas1.pack(side=LEFT, fill=BOTH, expand=1)
canvas2 = Canvas(tab2)
canvas2.pack(side=LEFT, fill=BOTH, expand=1)
# Add scrollbars to canvases
scrollbar1 = ttk.Scrollbar(tab1, orient=VERTICAL, command=canvas1.yview)
scrollbar1.pack(side=RIGHT, fill=Y)
scrollbar2 = ttk.Scrollbar(tab2, orient=VERTICAL, command=canvas2.yview)
scrollbar2.pack(side=RIGHT, fill=Y)
def _on_mousewheel(event):
canvas1.yview_scroll(int(-1*(event.delta/120)), "units")
canvas2.yview_scroll(int(-1*(event.delta/120)), "units")
# Bind mousewheel to scrollbars
canvas1.bind("<MouseWheel>", _on_mousewheel)
canvas2.bind("<MouseWheel>", _on_mousewheel)
# Configure the canvases scrollbars
canvas1.configure(scrollregion=canvas1.bbox('all'), yscrollcommand=scrollbar1.set)
canvas1.bind('<Configure>', lambda e: canvas1.configure(scrollregion = canvas1.bbox("all")))
canvas2.configure(scrollregion=canvas2.bbox('all'), yscrollcommand=scrollbar2.set)
canvas2.bind('<Configure>', lambda e: canvas2.configure(scrollregion = canvas2.bbox("all")))
# Create frames inside the canvases
inner_frame1 = ttk.Frame(canvas1)
inner_frame2 = ttk.Frame(canvas2)
# Add the frames to the canvases
canvas1.create_window((0,0), window=inner_frame1, anchor="nw")
canvas2.create_window((0,0), window=inner_frame2, anchor="nw")
# Add buttons to the frames
for thing in range(20):
ttk.Button(inner_frame1, text=f'Button {thing}').grid(row=thing, column=0, pady=10, padx=10)
ttk.Button(inner_frame2, text=f'Button {thing}').grid(row=thing, column=0, pady=10, padx=10)
Window.mainloop()
Change the function to this:
def _on_mousewheel(event):
event.widget.yview_scroll(int(-1*(event.delta/120)), "units")
It find the canvas that caught the event and changes its yview_scroll.
I also solved the second problem but I still don't know why exactly. Using this answer I figured out what works and a plausible explanation for it. You have to move your canvas.pack(...) statements after you have packed the scroll bar. So your code should look like this:
from tkinter import *
from tkinter import ttk
Window = Tk()
# Create the Outer Frame
outer_frame = Frame(Window)
outer_frame.pack(fill=BOTH, expand=1)
# Create and add the tabs
tab_control = ttk.Notebook(outer_frame)
tab1 = ttk.Frame(tab_control)
tab_control.add(tab1, text="Tab 1")
tab2 = ttk.Frame(tab_control)
tab_control.add(tab2, text="Tab 2")
tab_control.pack(expand=1, fill=BOTH)
# Create the canvases
canvas1 = Canvas(tab1, bg="orange")
canvas2 = Canvas(tab2, bg="light blue")
# Add scrollbars to canvases
scrollbar1 = ttk.Scrollbar(tab1, orient=VERTICAL, command=canvas1.yview)
scrollbar1.pack(side=RIGHT, fill=Y)
scrollbar2 = ttk.Scrollbar(tab2, orient=VERTICAL, command=canvas2.yview)
scrollbar2.pack(side=RIGHT, fill=Y)
# Pack the canvases after the scrollbar
canvas1.pack(side=LEFT, fill=BOTH, expand=True)
canvas2.pack(side=LEFT, fill=BOTH, expand=True)
# This is the updated even handler:
def _on_mousewheel(event):
event.widget.yview_scroll(int(-1*(event.delta/120)), "units")
# Bind mousewheel to scrollbars
canvas1.bind("<MouseWheel>", _on_mousewheel)
canvas2.bind("<MouseWheel>", _on_mousewheel)
# Configure the canvases scrollbars
canvas1.configure(scrollregion=canvas1.bbox('all'), yscrollcommand=scrollbar1.set)
canvas1.bind('<Configure>', lambda e: canvas1.configure(scrollregion = canvas1.bbox("all")))
canvas2.configure(scrollregion=canvas2.bbox('all'), yscrollcommand=scrollbar2.set)
canvas2.bind('<Configure>', lambda e: canvas2.configure(scrollregion = canvas2.bbox("all")))
# Create frames inside the canvases
inner_frame1 = ttk.Frame(canvas1)
inner_frame2 = ttk.Frame(canvas2)
# Add the frames to the canvases
canvas1.create_window((0,0), window=inner_frame1, anchor="nw")
canvas2.create_window((0,0), window=inner_frame2, anchor="nw")
# Add buttons to the frames
for thing in range(20):
ttk.Button(inner_frame1, text=f'Button {thing}').grid(row=thing, column=0, pady=10, padx=10)
ttk.Button(inner_frame2, text=f'Button {thing}').grid(row=thing, column=0, pady=10, padx=10)
Window.mainloop()
My guess is that when you first pack the canvas with expand=True, fill="both", it decided that it will try to take as much space as possible without thinking about the scrollbar. I don't really know why that happens.
Hello everyone i am learning to make a gui with tkinter but i run into something and can't find the answer to it i have 3 frames a header a shadow for the header and a main content frame and that works fine
code :
root = Tk()
root.geometry("1080x600")
root.minsize(width=1080, height=600)
root.maxsize(width=1080, height=600)
root.title("learning ui")
headerFrame = Frame(root, height=50, bg="#17181b")
headerShadow = Frame(root, height=3, bg="#08090a")
contentFrame = Frame(root, bg="#17181b")
headerFrame.pack(side=TOP, fill=X)
headerShadow.pack(fill=X)
contentFrame.pack(fill=BOTH, expand=True)
root.mainloop()
Screen shot :
But when i pack something to the header it looses its height.
code:
root = Tk()
root.geometry("1080x600")
root.minsize(width=1080, height=600)
root.maxsize(width=1080, height=600)
root.title("learning ui")
headerFrame = Frame(root, height=50, bg="#17181b")
headerShadow = Frame(root, height=3, bg="#08090a")
contentFrame = Frame(root, bg="#17181b")
headerFrame.pack(side=TOP, fill=X)
headerShadow.pack(fill=X)
contentFrame.pack(fill=BOTH, expand=True)
main.e = Entry(headerFrame)
main.e.pack(side=RIGHT)
main.e.focus_set()
searchBtn = Button(headerFrame, text="Search", command=lambda: callback(retrieve_input()))
searchBtn.pack(side=RIGHT)
def callback(q):
print q
root.mainloop()
Screen shot:
All widgets are designed to "shrink to fit" their children. While this may seem counter-intuitive at first, it's the best way to do widget layout. Don't try to force a frame or window to be a specific size, just put the widgets in that you want and it will end up being the right size.
If you want to turn this feature off you can, but I strongly recommend against it. If you want to know more, search for "overrideredirect".
I have the following interface: 3 frames from which 2 are on the left-hand, frame1 and frame2 and another one which is on the right, frame3.
I want to create another frame, frame4 which will have the label Output and under it there should be a Listbox. I want that both these widgets to span over the previous frames, each being places in a cell by using the grid manager.
Also I am not sure if Listbox is the widget I should be using. I want something which will contain the output of the program I will run through my application. I also thought of Entry but I need something in which I can display more than one line.
This is the code I have so far:
from Tkinter import *
root = Tk()
frame1 = Frame(root)
frame1.grid(row=0,column=0)
frame2 = Frame(root)
frame2.grid(row=1,column=0)
frame3 = Frame(root)
frame3.grid(row=0,column=1)
frame4 = Frame(root)
frame4.grid(row=2,columnspan=2)
l5 = Label(frame4, text='Output:').grid(row=2,columnspan=2)
output = Listbox(frame4, height=5)
#output.grid(row=2,column=0,columnspan=2)
#output.pack(side=LEFT, fill=BOTH, expand=1)
root.mainloop()
I managed to make the label to span across the other frames, but when I uncommented either of the last 2 lines, the interface didn't open and the program just froze.
How can I solve this?
I slightly ammended your code:
from tkinter import * # for python 2.7 use Tkinter
root = Tk()
frame1 = Frame(root, bg='red', height=20)
frame1.grid(row=0,column=0, sticky=W+E)
frame3 = Frame(root, bg='blue', height=20)
frame3.grid(row=0,column=1, sticky=W+E)
frame2 = Frame(root, bg='green', height=20)
frame2.grid(row=1,column=0, sticky=W+E)
frame4 = Frame(root)
frame4.grid(row=2,columnspan=2, sticky=E+W)
l5 = Label(frame4, text='Output:', bg='orange').grid(row=0, column=0, sticky=E+W)
output = Listbox(frame4, height=5, width=50)
output.grid(row=1,column=0)
#output.pack(side=LEFT, fill=BOTH, expand=1)
root.mainloop()
This results in:
So basically what I did was to add bg, height and sticky parameters to frames and label to easily visual what is happening and how they frames are laid out. Also I modified grid parameters listbox and label.
Hope this is what you are after, or it will help you to get there.
If you use both .grid() and .pack() Tkinter (python 2.x) or tkinter (python 3.x) will happily spend the rest of your life trying to find a way to satisfy both, you must use one within the same window or Frame, see http://www.openbookproject.net/py4fun/gui/tkPhone.html