Buttons and Labels not placing properly on frame - python

I am really new to coding and python and this could be just a lack of basic knowledge, but every time I run the code it displays the Buttons and Labels in the default top left position. Does anyone know where the mistake is or how to fix this issue?
I initially tried this with .grid(row = 1, column = 5) but this also didn't work.
def swap(frame):
frame.tkraise()
for frame in (f1, f2, f3):
frame.grid(row = 0, column = 0)
# ---------------------------------------1
# This is just the first frame but I think this is scaleable
name1 = Label(f1, text = "Frame 1")
name1.pack(side = BOTTOM)
switchbutton1 = Button(f1, text = "Next Frame", command = lambda: swap(f2))
switchbutton1.pack(side = BOTTOM)
I don't get any error message but it only displays in the default position.
I expect that I can put the buttons with .grid() or .pack() in a custom position.

Related

Labels Added via Button Command Not Expanding Frame

I am trying to dynamically add labels to a frame contained within a canvas, for scrollbar capability. The labels are being added via a function that is called from a button. The function works fine if called on startup, the frame updates as expected and I have scrollbar capabilities. If I call the function from the button command the frame updates with the labels, but only up to the limit of the starting frame/canvas size. The additional area of the frame containing the rest of the labels won't be visible as the scrollbar isn't "activated"
I tried searching but couldn't find this question being asked before. I am new to Python and Tkinter so apologies if I'm missing something obvious.
from tkinter import *
from tkinter import ttk
root = Tk()
root.geometry("550x550")
main_frame = Frame(root)
main_frame.grid(row = 0, column = 0)
button_frame = Frame(root)
button_frame.grid(row = 1, column = 0)
image_canvas_0 = Canvas(main_frame, height = 500, width = 500)
image_canvas_0.grid(row = 0, column = 0)
image_canvas_0_scrollbar = ttk.Scrollbar(main_frame, orient = VERTICAL, command = image_canvas_0.yview)
image_canvas_0_scrollbar.grid(column = 1, row = 0, sticky = (N,S))
image_canvas_0.config(yscrollcommand = image_canvas_0_scrollbar.set)
image_canvas_0.bind('<Configure>', lambda e: image_canvas_0.configure(scrollregion = image_canvas_0.bbox("all")))
second_frame = Frame(image_canvas_0)
image_canvas_0.create_window((0,0), window = second_frame, anchor = 'nw')
def test_function(*args):
for i in range(100):
label_text = 'test' + str(i)
Label(second_frame, text = label_text).grid(row = i, column = 0)
func_button = Button(button_frame, text = 'Click Me', command = test_function)
func_button.grid(row = 0, column = 0)
#test_function()
root.mainloop()
You aren't changing the scrollregion after you've added widgets to the frame. The canvas doesn't know that there's new data that should be scrollable.
Normally this is done by adding a bind to the frame's <Configure> event, since that event fires whenever the frame changes size.
second_frame.bind("<Configure>", lambda e: image_canvas_0.configure(scrollregion = image_canvas_0.bbox("all")))
The reason your code seems to work when calling the function directly at startup is that you have a similar binding on the canvas itself, which automatically fires once the widget is actually shown on the screen. That happens after you call test_function() when mainloop first starts to run. Once the program starts, the canvas <Configure> event doesn't fire a second time.

Scrollable page in Tkinter Notebook

For a project I need to specify a certain value for N subfiles (sets of data), and this value can either be evenly spaced (omitted here), requiring only a starting value and an increment, or unevenly spaced, which means each subfile has its own value. I've decided to use a Notebook to separate the two methods of entry.
As the number of subfiles can get into hundreds, I would need a scrollbar, and after consulting Google I've found out that to use a scrollbar in such manner I would need to use a canvas and place a frame in it with everything I would want to scroll through.
The number can vary each time, so I decided to use a dictionary, that would be iteratively filled, to contain all 'entry frames' that each contain a label, an entry field and a variable, rolled up into one custom class IterEntryField. After a class instance is created, it's packed inside one container frame. After the for loop is over, the container frame is placed on a canvas and the scrollbar is given a new scrollregion.
from tkinter import *
from tkinter.ttk import Notebook
N = 25
class IterEntryField:
def __init__(self, frame, label):
self.frame = frame
self.label = label
def pack(self):
self.valLabel = Label(self.frame, text = self.label, anchor = 'w')
self.valLabel.pack(fill = X, side = LEFT)
self.variable = StringVar()
self.variable.set('0')
self.valEntry = Entry(self.frame, textvariable = self.variable)
self.valEntry.pack(fill = X, side = RIGHT)
def notebookpopup():
zSetupWindow = Toplevel(root)
zSetupWindow.geometry('{}x{}'.format(800, 300))
notebook = Notebook(zSetupWindow)
evspace = Frame(notebook)
notebook.add(evspace, text = "Evenly spaced values")
sOverflow = Label(evspace, text = 'Ignore this')
sOverflow.pack()
uevspace = Frame(notebook)
notebook.add(uevspace, text = "Individual values")
canvas = Canvas(uevspace, width = 800, height = 400)
vsb = Scrollbar(canvas, command=canvas.yview)
canvas.config(yscrollcommand = vsb.set)
canvas.pack(side = LEFT, fill = BOTH, expand = True)
vsb.pack(side = RIGHT, fill = Y)
entryContainer = Frame(canvas)
entryContainer.pack(fill = BOTH)
frameDict = {}
for i in range(0, N):
frameDict[i] = Frame(entryContainer)
frameDict[i].pack(fill = X)
entry = IterEntryField(frameDict[i], 'Z value for subfile {}'.format(i+1))
entry.pack()
canvas.create_window(200, 0, window = entryContainer)
canvas.config(scrollregion = (0,0,100,1000))
notebook.pack(fill = X)
root = Tk()
button = Button(root, text = 'new window', command = notebookpopup)
button.pack()
root.mainloop()
I'm having three problems with this code:
The pages are incredibly short, only showing a couple lines.
I can't figure out the "proper" offset in create_window. I thought 0, 0 would place it in upper left corner of the canvas, but apparently the upper left corner of the window is taken instead. This could probably fixed by some reverse of the canvasx and canvasy methods, but I haven't been able to find any.
The entry fields and labels are cramped together instead of taking up the entire width of the canvas. This wasn't a problem when I only used the notebook page frame as the container.
Your first problem goes back to how you pack your notebook. Simply change notebook.pack(...) to below:
notebook.pack(fill="both", expand=True)
The second one can be solved by specifying the anchor position in your create_window method:
canvas.create_window(0, 0, window = entryContainer, anchor="nw")
I don't understand what the 3rd problem is - it looks exactly as expected.

How do I create multiple similar frames with tkinter using classes?

How would I create multiple frames that have the same widgets in Tkinter? Basically what I want to do is create 15 copies of a set of multiple frames that all contain the same widgets as shown in the image, the purpose of this program is to assist the user in sorting photographs into groups based on a specific ID supplied by the user. The radio buttons are there for the user to classify each photo, i.e front, back ,top etc..
Its not very efficient to copy the code 15 times and I want to know if it's possible to use a class to define the frame once and reuse the code for each new frame. I need to keep track of what the user does on each frame and save their selections on the radio buttons and check boxes for each frame. After all the photos have been classified by the user, a button is clicked that should then save all the photos with a new ID and also saves the info from the radio buttons into a csv file. Then the next batch of photos is loaded and the process repeats.
I have included an example of the code I used to create one of the frames, this is the code that I want to make reusable. I do not want to have to repeat it 15 times.
############################################################################
#FRAME 3
Photo_2 = Frame(master, bg = "white",relief = RIDGE, bd = 2)
Photo_2.grid(column = 2, row = 1, padx=5, pady=5)
Lbl2 = Label(Photo_2,text = 'Frame 3')
Lbl2.grid(row = 0, column = 0, columnspan = 4, pady = 5)
# Check box
varc2 = StringVar()
varc2.set(0)
Check_2 = Checkbutton(Photo_2, variable = varc2, text="Relevant?", command = lambda:Chk_Val(varc2))
Check_2.grid(row = 1,column = 0,columnspan = 4)
# Photo 1
P2 = "Photo_2.jpg"
P2 = Image.open(P2).resize((200, 200), Image.ANTIALIAS)
phot2 = ImageTk.PhotoImage(P2)
panel = Label(Photo_2, image = phot2)
panel.grid(columnspan = 3, column=1)
# Create Multiple Radio Buttons
Rad_Cont = Frame(Photo_2)
Rad_Cont.grid(column = 0, row = 2)
v2 = StringVar()
v2.set("Address")
for text,mode in RADIO:
b = Radiobutton(Rad_Cont, text=text, variable=v2,
value=mode, command = lambda:Rad_Val(v2))
b.pack()
################################################################################
Of course it is possible to create a class to represent similar objects.
Here is how I might implement what you're trying to accomplish:
import tkinter as tk
class PhotoFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master, bg='white', relief='ridge', bd=2)
self.label_widget()
self.checkbox_widget()
self.photo_widget()
self.radio_widgets()
def label_widget(self):
self.title_label = tk.Frame(self, text='Frame 3') # Or Frame 1, 2 etc.
self.title_label.grid(row=0, column=0, columnspan=4, pady=5)
def checkbox_widget(self):
self.varc = tk.StringVar()
self.varc.set(0)
self.checkbox = tk.Checkbutton(self, variable=self.varc,
text='Relevant?', command=self.Chk_Val)
self.checkbox.grid(row=1, column=0, columnspan=4)
def photo_widget(self):
# ... Your code here
def radio_widgets(self):
# ... Your code here
def Chk_Val(self):
# ... Your code here
Now I tried not to give you the entire solution so you can learn and figure the rest out by yourself, but I'm sure you can see what I'm getting at in terms of using a class. Now this class PhotoFrame could be used as many times as you'd like, although please understand you'll have to configure each frame appropriately, e.g. I would omit the the text attribute assignment in the label_widget section (You don't want all of your frames titled 'Frame 3'), so in your main program logic, you'd configure each instance of a PhotoFrame object like so:
frame1 = PhotoFrame(master)
frame1.title_label.configure(text='Frame 1') # Along with any other configuration
I hope this all helps - if you are not familiar with classes check out the provided documentation, and here is a great tkinter reference: NMT tkinter

ttk .grid(sticky = 'ew') with .grid_columnconfigure doesn't work properly for notebook entries

I'm using Tkinter and ttk in Python 2.7.11 to create a GUI, which is basically a converter for datasets. This part is working without a problem. The help window I'm programming however, gives me headaches.
Currently the help is a Tkinter.Toplevel and structured via Notebooks, which include ttk.Frames, ttk.LabelFrames and ttk.Labels. My problem here is, that the frames of the different notebooks and the labelframes with the labels inside don't stick properly. If for example one entry is 50 characters long and all other entries are 20 characters long, the whole window will stretch to 50 characters (which is fine), but the frames and labels containing the 20 chars will not propagate, if they are not within the same notebookentry.
I honestly don't know, if I'm using the weight wrong or if I'm overlooking some other detail.
You can see some example code without labelframes here (no labelframes to shorten the example code):
# -*- coding: utf-8 -*-
import Tkinter
import ttk
import align_text
class NotebookApp:
# Initiate modules
def __init__(self, parent):
self.my_parent = parent
self.globalStyle()
self.myNotebookEntries()
self.myNotebook()
self.closeButton()
# Set global style:
def globalStyle(self):
# Style:
style_global = ttk.Style()
style_global.theme_use('winnative')
# Create and define the notebook:
def myNotebook(self):
bd_width = 2
relief = 'ridge'
# Top Notebook:
top_notebook = ttk.Notebook(self.my_parent)
top_notebook.grid(column = 0, row = 0)
# Tabs of top Notebook:
first_tab_of_top = ttk.Frame(top_notebook)
second_tab_of_top = ttk.Frame(top_notebook)
# Notebook of tab one:
nb_one_of_top = ttk.Notebook(first_tab_of_top)
nb_one_of_top.grid(column = 0, row = 0)
# Notebook of tab two:
nb_two_of_top = ttk.Notebook(second_tab_of_top)
nb_two_of_top.grid(column = 0, row = 0)
# Tabs of the Notebook of tab one:
first_tab_of_nb_one = ttk.Frame(nb_one_of_top)
first_tab_of_nb_one.grid(column = 0, row = 0, sticky = 'ew')
first_tab_of_nb_one.grid_columnconfigure(0, weight = 1)
second_tab_of_nb_one = ttk.Frame(nb_one_of_top)
second_tab_of_nb_one.grid(column = 0, row = 0, sticky = 'ew')
second_tab_of_nb_one.grid_columnconfigure(0, weight = 1)
# Tabs of the Notebook of tab two:
first_tab_of_nb_two = ttk.Frame(nb_two_of_top)
first_tab_of_nb_two.grid(column = 0, row = 0, sticky = 'ew')
first_tab_of_nb_two.grid_columnconfigure(0, weight = 1)
second_tab_of_nb_two = ttk.Frame(nb_two_of_top)
second_tab_of_nb_two.grid(sticky = 'ew')
second_tab_of_nb_two.grid_columnconfigure(0, weight = 1)
# Adding the Tabs (Frames) of the top Notebook:
top_notebook.add(first_tab_of_top, text = self.first_tab_of_top_txt)
top_notebook.add(second_tab_of_top, text = self.second_tab_of_top_txt)
# Adding the Tabs (Frames) of the Notebook of tab one of the top Notebook:
nb_one_of_top.add(first_tab_of_nb_one, text = self.first_tab_of_nb_one_txt)
nb_one_of_top.add(second_tab_of_nb_one, text = self.second_tab_of_nb_one_txt)
# Adding the Tabs (Frames) of the Notebook of tab two of the top Notebook:
nb_two_of_top.add(first_tab_of_nb_two, text = self.first_tab_of_nb_two_txt)
nb_two_of_top.add(second_tab_of_nb_two, text = self.second_tab_of_nb_two_txt)
# Add Labels to the Tabs (Frames) of the Notebook of tab one of the top Notebook:
label_one_of_nb_one_of_top = ttk.Label(first_tab_of_nb_one,
text = '\n'.join(align_text.align_paragraph(
self.label_one_of_nb_one_of_top_txt,
width = 50,
debug = 0)),
borderwidth = bd_width,
relief = relief)
label_one_of_nb_one_of_top.grid(column = 0, row = 0, sticky = 'nsew')
label_one_of_nb_one_of_top.grid_columnconfigure(0, weight = 1)
label_two_of_nb_one_of_top = ttk.Label(second_tab_of_nb_one,
text = self.label_two_of_nb_one_of_top_txt,
borderwidth = bd_width,
relief = relief)
label_two_of_nb_one_of_top.grid(column = 0, row = 0, sticky = 'nsew')
label_two_of_nb_one_of_top.grid_columnconfigure(0, weight = 1)
# Add Labels to the Tabs (Frames) of the Notebook of tab two of the top Notebook:
label_one_of_nb_two_of_top = ttk.Label(first_tab_of_nb_two,
text = self.label_one_of_nb_two_of_top_txt,
borderwidth = bd_width,
relief = relief)
label_one_of_nb_two_of_top.grid(column = 0, row = 0, sticky = 'nsew')
label_one_of_nb_two_of_top.grid_columnconfigure(0, weight = 1)
label_two_of_nb_two_of_top = ttk.Label(second_tab_of_nb_two,
text = self.label_two_of_nb_two_of_top_txt,
borderwidth = bd_width,
relief = relief)
label_two_of_nb_two_of_top.grid(column = 0, row = 0, sticky = 'nsew')
label_two_of_nb_two_of_top.grid_columnconfigure(0, weight = 1)
# All notebookentries:
def myNotebookEntries(self):
self.first_tab_of_top_txt = "First Notebook"
self.second_tab_of_top_txt = "Second Notebook"
self.first_tab_of_nb_one_txt = "First Tab"
self.second_tab_of_nb_one_txt = "Second Tab"
self.first_tab_of_nb_two_txt = "First Tab"
self.second_tab_of_nb_two_txt = "Second Tab"
self.label_one_of_nb_one_of_top_txt = ("This is the text of the first "
"label of the first notebook."
"It is somewhat longer, than the "
"second one.")
self.label_two_of_nb_one_of_top_txt = "Text of Label two of notebook one."
self.label_one_of_nb_two_of_top_txt = "Text of Label one of notebbok two."
self.label_two_of_nb_two_of_top_txt = "Text of Label two of notebbok two."
# Button to close the notebook:
def closeButton(self):
close_button = ttk.Button(self.my_parent, text = "Close App",
command = self.closeButtonClick)
close_button.bind('<Return>', self.wrapperCloseButton)
close_button.grid(column = 0, row = 1)
# Command, used by the close button:
def closeButtonClick(self):
self.my_parent.destroy()
def wrapperCloseButton(self, event):
self.closeButtonClick()
# Main:
if __name__ == '__main__':
root = Tkinter.Tk()
root.title("Simple Notebbok")
notebook_app = NotebookApp(root)
root.mainloop()
I've also read about two seemingly similar issue, but with the distribution of frames and buttons. In both cases the proper use of weight helped to achieve the goals of each programmer. Since I'm writing a help for the users of my converter, there is multiline-text, which is separated by the align_paragraph function of the recipe 414870 by Denis Barmenkov. However, there seems to be no difference regarding the sticky property, whether I use this function to autosize my text or not. There seems also no difference between using grid_columnconfigure or columnconfigure.
You seem to have a misunderstanding of what column weights do, and how to use them, and how the sticky attribute interacts with columns.
The role of column and row weights
The weight of a column only affects how excess space is allocated to a column. If a column has a weight, it is given some (or all) of the extra space. Also, the weight only affects interior widgets, not the widget itself. For example, foo.grid_columnconfigure(weight=1) only affects widgets placed inside foo, and has no affect on how foo is allocated space inside its parent.
Configure the weights of parent rows and columns
Your first problem seems to be that you don't give any weight to any columns in the window as a whole (ie: the parent of top_notebook). If you want top_notebook to be given extra space in the parent (grow and shrink with the window), you need to give weight to the rows and columns of its parent.
For example:
self.my_parent.grid_columnconfigure(0, weight=1)
self.my_parent.grid_rowconfigure(0, weight=1)
Since top_notebook is in self.my_parent you must give columns of self.my_parent weight if you want the notebook to be able to fill the parent. If you want the notebook to fill all of the extra space allocated to it, you must give it the sticky attribute:
top_notebook.grid(column = 0, row = 0, sticky="nsew")
Don't give weights to widgets that don't have children
You have another bit of misunderstanding with these lines of code:
label_one_of_nb_one_of_top.grid_columnconfigure(0, weight = 1)
...
label_one_of_nb_two_of_top.grid_columnconfigure(0, weight = 1)
...
label_one_of_nb_two_of_top.grid_columnconfigure(0, weight = 1)
...
label_two_of_nb_two_of_top.grid_columnconfigure(0, weight = 1)
What you are doing is telling grid that any extra space inside the label should be allocated to widgets in column 0 inside the label. However, there are no other widgets inside the label, so this has absolutely no effect. Remember, grid_rowconfigure and grid_columnconfigure only affect child widgets.
Don't rely strictly on grid
grid is great, but it sometimes requires more work to set up. pack is a much better choice in the case where you have a single widget inside another widget, and you want that inside widget to fill its parent. It's also good if your are arranging widgets in a single row (eg: toolbar) or column (eg: a toolbar / main work area / statusbar arranged top-to-bottom), but that's not particularly relevant to your code.
For example, each of your "top" tabs has only a single child, which is another notebook. This is a perfect place to use pack, because you can do everything in one line of code:
# Notebook of tab one:
nb_one_of_top = ttk.Notebook(first_tab_of_top)
nb_one_of_top.pack(fill="both", expand=True)
# Notebook of tab two:
nb_two_of_top = ttk.Notebook(second_tab_of_top)
nb_two_of_top.pack(fill="both", expand=True)
To use grid properly you must give a weight to row 0 and column 0 of each tab, adding four lines of extra code.

Changing place of boxes tkinter

I am creating an app and this is the part of it. I will put a canvas up to the screen so I want these variables to be at the bottom of the page but when I run this code it places everything on top left corner as shown in the photo(figure1). I am using grid function but I couldn't understand why it doesn't place where I want.(Sorry for bad english)
main = Tkinter.Tk()
main.geometry("1500x1000")
main.title("Momentum")
ml1 = Tkinter.Label(main, text ="MASS OF THE FIRST OBJECT")
ml2 = Tkinter.Label(main, text ="MASS OF THE SECOND OBJECT")
me1 = Tkinter.Entry(main)
me2 = Tkinter.Entry(main)
ml1.grid(row=600, column =400)
ml2.grid(row =600, column =600 )
me1.grid(row=650, column =400)
me2.grid(row=650, column=600)
main.mainloop()

Categories