How can I make two Tkinter ttk.Frame widgets the same width? - python

I'm working on a Tkinter GUI. I want to make two separate frames have the same width so that they line up vertically. I do not know of any way to put them inside the same structure because they need to be independently scrollable in the vertical direction (that functionality is not yet in the code).
I tried various ways of what I thought was setting the lower frame width equal to the upper frame width because the lower frame has a smaller width by default, but that seems to have no effect.
See lines
# Match widths
self.vert_frames[-1][1].configure(width=self.vert_frames[-1][0].cget("width"))
I'm new to both stackoverflow and python, so let me know if you need more details or have corrections to my question formatting or anything else. I've been a longtime reader, so I'm excited to hear what solutions you all know about!
Thank you all in advance.
# Imports
import tkinter as tk
from tkinter import ttk
# Define GUI CLASS
class tl_GUI:
# initialize the GUI
def __init__(self, root):
self.root = root
# set geometry
# self.root.geometry("480x320+150+150") # remove if not used in final version
self.root.grid_rowconfigure(0, weight=1)
self.root.grid_columnconfigure(0, weight=1)
# SET STRUCTURE
# create data_canvas
self.data_canvas = tk.Canvas(self.root)
self.data_canvas.grid(row = 0, column = 0, sticky = "nsew")
# create 2-pane PanedWindow
self.data_panedwindow = ttk.Panedwindow(self.data_canvas, orient="vertical")
self.data_panedwindow.pack(fill="both", expand=True)
# create input canvas
self.in_canvas = tk.Canvas(self.data_panedwindow)
self.data_panedwindow.add(self.in_canvas, weight=1)
# create input frame
self.in_frame = ttk.Frame(self.in_canvas, relief="sunken")
self.in_frame.pack()
# create output canvas
self.out_canvas = tk.Canvas(self.data_panedwindow)
self.data_panedwindow.add(self.out_canvas, weight=1)
# create output frame
self.out_frame = ttk.Frame(self.out_canvas, relief="sunken")
self.out_frame.pack()
# CREATE INITIAL I/O FRAMES
self.curr_compares = 0
self.vert_frames = []
for init_frames in range(0, 2):
self.add_compareIO()
# define method to add a vertical I/O frame
def add_compareIO(self):
# create input and output frames
self.vert_frames.append([])
self.vert_frames[-1].append(ttk.Frame(self.in_frame, padding = 2, relief = "groove"))
self.vert_frames[-1].append(ttk.Frame(self.out_frame, padding = 2, relief = "groove"))
self.vert_frames[-1][0].grid(row = 0, column = self.curr_compares)
self.vert_frames[-1][1].grid(row = 0, column = self.curr_compares)
# close_frame
col = len(self.vert_frames)-1 # may be able to use self.curr_compares instead
self.vert_frames[-1][0].button_close = ttk.Label(self.vert_frames[-1][0],
text="[Image here]")
self.vert_frames[-1][0].button_close.grid(row = 0, column = 0, columnspan=2, stick = "e")
self.vert_frames[-1][0].button_close.bind("<Button-1>",
lambda e: self.destroy_frame(col_num=col))
self.vert_frames[-1][1].button_close = ttk.Label(self.vert_frames[-1][1],
text="[Image here]")
self.vert_frames[-1][1].button_close.grid(row = 0, column = 0, columnspan=2, stick = "e")
self.vert_frames[-1][1].button_close.bind("<Button-1>",
lambda e: self.destroy_frame(col_num=col))
# populate inputs
self.vert_frames[-1][0].label_hpp = ttk.Label(self.vert_frames[-1][0], text = "INPUT1")
self.vert_frames[-1][0].entry_hpp = ttk.Entry(self.vert_frames[-1][0], width = 13)
self.vert_frames[-1][0].entry_hpp.bind("<KeyRelease>", lambda e: self.refresh_calculations(col)) # recalculate when any keyboard button is pressed
self.vert_frames[-1][0].label_int_rate = ttk.Label(self.vert_frames[-1][0], text = "INPUT2")
self.vert_frames[-1][0].entry_int_rate = ttk.Entry(self.vert_frames[-1][0], width = 13)
self.vert_frames[-1][0].entry_int_rate.bind("<KeyRelease>", lambda e: self.refresh_calculations(col))
self.vert_frames[-1][0].label_RENAME = ttk.Label(self.vert_frames[-1][0], text = "INPUT3")
self.vert_frames[-1][0].entry_RENAME = ttk.Entry(self.vert_frames[-1][0], width = 13)
self.vert_frames[-1][0].entry_RENAME.bind("<KeyRelease>", lambda e: self.refresh_calculations(col))
self.vert_frames[-1][0].label_hpp.grid(row = 1, column = 0)
self.vert_frames[-1][0].entry_hpp.grid(row = 1, column = 1)
self.vert_frames[-1][0].label_int_rate.grid(row = 2, column = 0)
self.vert_frames[-1][0].entry_int_rate.grid(row = 2, column = 1)
self.vert_frames[-1][0].label_RENAME.grid(row = 3, column = 0)
self.vert_frames[-1][0].entry_RENAME.grid(row = 3, column = 1)
# populate outputs
self.vert_frames[-1][1].label_tcl = ttk.Label(self.vert_frames[-1][1], text = "OUTPUT1: {}".format(10))
self.vert_frames[-1][1].label_tcl.grid(row = 1, column = 0)
# Match widths
self.vert_frames[-1][1].configure(width=self.vert_frames[-1][0].cget("width"))
# update counter
self.curr_compares += 1
def destroy_frame(self, col_num):
self.vert_frames[col_num][0].grid_forget()
self.vert_frames[col_num][1].grid_forget()
self.vert_frames[col_num][0].destroy()
self.vert_frames[col_num][1].destroy()
self.vert_frames[col_num] = []
# define method to refresh calculations (wrapper for actual calcs)
def refresh_calculations(self, col):
pass
# Execute code
if __name__ == "__main__":
root = tk.Tk()
my_gui = tl_GUI(root)
root.mainloop()

Related

tkinter making things next to each other

I am trying to position three Entries next to each other MM, TT and CVC it is for a project. However I can achieve this result, I have tried using sticky and columnspan but from my own understanding these will not work. Any solutions would be helpful
Currently I have:
from tkinter import *
Master = Tk(); Master.attributes("-fullscreen", True)
Page = Frame(Master)
Page.place(relx = 0.5, rely = 0.5, anchor = "center")
CradnumberEntry = Entry(Page)
CradnumberEntry.insert(0, "Card number")
CradnumberEntry.config(width = int(2 * CradnumberEntry.cget("width")))
CradnumberEntry.grid(row = 0, column = 0)
MMEntry = Entry(Page)
MMEntry.insert(0, "MM")
MMEntry.grid(row = 1, column = 0, sticky = "w")
MMEntry.config(width = int(1 / 4 * MMEntry.cget("width")))
YYEntry = Entry(Page)
YYEntry.insert(0, "YY")
YYEntry.grid(row = 1, column = 1, sticky = "w")
YYEntry.config(width = int(1 / 4 * YYEntry.cget("width")))
CVCEntry = Entry(Page)
CVCEntry.insert(0, "CVC")
CVCEntry.grid(row = 1, column = 2, sticky = "w")
CVCEntry.config(width = int(1 / 4 * CVCEntry.cget("width")))
One very simply solution to this specific layout problem is to conceptualize your GUI as four columns. The first column contains "MM", the next "YY", the third "CVC", and the fourth is to take up all extra space. Then, make sure the "Card Number" field spans all four columns.
To to this requires changing one line of your code, and adding one line:
CradnumberEntry.grid(row = 0, column = 0, columnspan=4)
Page.grid_columnconfigure(3, weight=1)
Another very common solution is to put the widgets that are on the second row into a frame. The "Card Number" entry goes at the top, and this other frame goes below it. Then, you can use pack to put the "MM", "YY", and "CVC" entries on the left side of this bottom frame.
second_row = Frame(Page)
second_row.grid(row = 1, column = 0, sticky="ew")
This also requires that the parent of the other widgets is this second frame:
MMEntry = Entry(second_row)
YYEntry = Entry(second_row)
CVCEntry = Entry(second_row)
The easiest thing to do is probably to pack all of those entries in a containing Frame widget
cc_frame = Frame(Page) # create a container
cc_frame.grid(row=1, column=0)
MMEntry = Entry(cc_frame) # use 'cc_frame' as this widget's parent
MMEntry.insert(0, "MM")
MMEntry.pack(expand=False, fill='none')
MMEntry.config(width = int(1 / 4 * MMEntry.cget("width")))
YYEntry = Entry(cc_frame) # use 'cc_frame' as this widget's parent
YYEntry.insert(0, "YY")
YYEntry.pack(expand=False, fill='none')
YYEntry.config(width = int(1 / 4 * YYEntry.cget("width")))
CVCEntry = Entry(cc_frame) # use 'cc_frame' as this widget's parent
CVCEntry.insert(0, "CVC")
CVCEntry.pack(expand=False, fill='none')
CVCEntry.config(width = int(1 / 4 * CVCEntry.cget("width")))
BTW, you should avoid variable names with CapitalLetters in Python. This syntax is usually reserved for class names (like Entry and Frame for example). Instead, you should use snake_case

Displaying images iteratively in tkinter

# Generates a frame that also contains all the necessary widgets for all mounts in a list
# Takes the parent window the frame will be part of (TK()),
# the row (int) and column (int) the new frame will be located on the parent window,
# the name of the frame we're creating (string),
# and a list of all the mounts to go into the frame (list of string),
# the complement_checkbox (tkinter.CheckButton())
# Returns (tkinter.LabelFrame)
def generate_widgets(master, frame_name, mount_list, complement_checkbox):
new_frame = LabelFrame(master, text = frame_name, font=("Arial Narrow", 18), padx = 5, pady = 5)
new_frame.grid(row = 0, column = 0, padx = 10, pady = 10, sticky = N + S + E + W)
# We have row weight be equal to the number of mounts per frame
master.rowconfigure(0, weight = len(mount_list))
master.columnconfigure(0, weight = 1)
label_widgets = {}
image = {}
icon_label = {}
attempts_string = {}
spin_widgets = {}
button_widgets = {}
for i in range(len(mount_list)):
full_name = mount_list[i]
mount_name = full_name.split(' - ')[1]
label_widgets[i] = Label(new_frame, text = mount_list[i], font = ("Arial Narrow", 12))
label_widgets[i].grid(row = i, column = 0, sticky = W)
image[i] = PhotoImage(file = "Assets\\" + mount_name + ".png")
icon_label[i] = Label(new_frame, image = image[i])
icon_label[i].grid(row = i, column = 1, sticky = E)
attempts_string[i] = StringVar()
attempts_string[i].set(load_attempts(mount_name))
spin_widgets[i] = Spinbox(new_frame, from_ = 0, to = 1024, width = 5, textvariable = attempts_string[i])
spin_widgets[i].grid(row = i, column = 2, sticky = E)
button_widgets[i] = Button(new_frame,
text = "Calculate",
# j = i saves the current value of i into j when lambda is defined,
# if we don't have this line, the command will always use the value of i when the
# command is called, which will be the last row (i-th)
command = lambda j = i: open_and_save(mount_list[j],
mount_list[j].split(' - ')[1],
spin_widgets[j].get(),
complement_checkbox.get()))
button_widgets[i].grid(row = i, column = 3, sticky = E)
new_frame.rowconfigure(i, weight = 1)
# Column 0 is label, column 1 is spinbox, column 2 is button
new_frame.columnconfigure(0, weight = 1)
new_frame.columnconfigure(1, weight = 1)
new_frame.columnconfigure(2, weight = 1)
new_frame.columnconfigure(3, weight = 1)
return new_frame
I'm using the above code to create a new frame and fill it iteratively with widgets. So the problem I'm having right now is that the images I'm trying to display with icon_label[i] isn't working, and is instead just a blank space. I'm quite sure why this is happening, as when I try adding an image to the frame after the function has already run, the image display. I also noticed that if I make images{} and icon_label{} global, some of the images display, but not all (some frames have none, some only have the last few, or only the last). So I'm just wondering what is going on?

How do I put widgets inside a frame inside a window?

so I'm trying to put together an GUI divided up into multiple frames, and then have each frame have either labels or inputs, however, on the 'inputframe', whenever I add the label and try to place it, it just plops the label in the middle of the frame and I can't seem to move it anywhere. Is it possible for me to place labels inside a frame how I like?
I'm doing this on VS Code, have made progress on every other problem I've run into so far but can't seem to solve this one.
class Window1:
def __init__(self):
window = Tk() # Create a window
window.title("Order Processing Application") # Set title of a window
window.geometry('920x560')
#set up window and import picture for the window
photo = PhotoImage(file=r"C:\Users\Owner\Downloads\GWlogo.gif")
logoFrame = Frame(window, width = 160, height = 160)
logoFrame.grid(row = 0, column = 0)
stepTitleFrame = Frame(window, width = 840, height = 160, bg = 'red')
stepTitleFrame.grid(row = 0, column = 1)
statusFrame = Frame(window, width = 320, height = 560, bg='green')
statusFrame.grid(row = 1, column = 0)
inputFrame = Frame(window, width = 840, height = 560)
inputFrame.grid(row = 1, column = 1)
#imports Glamping World logo into the logo frame
logoPic = Label(logoFrame, image = photo)
logoPic.grid(row = 0, column = 0)
#Testing labels in the main input frame
inputLabel1 = Label(inputFrame, text="Testing Label 1")
inputLabel1.grid(row = 0, column = 1)
I'm trying to get the label to appear in the inputFrame, row 0 column 0, but it will only appear in the middle of the inputFrame.

Code runs but Tkinter window stays blank

The problem i'm having is that it even though it executes, the tkinter window remains blank. and as a side issue the window doesn't refresh to update the Actions checkbutton.
from tkinter import *
Pieces = {}
Actions =[]
class GameCreation(Frame):
def __init(self,master):
super(GameCreation,self).__init__(master)
self.grid()
self.CreatePiece()
#Creating pieces function
def CreatePiece(self):
Label(self,text ="What piece are we working with?").grid(row =0,
column = 0,
sticky = W)
self.piece_entry = Entry(self)
self.piece_entry.grid(row =0,
column = 1,
sticky = W)
Label (self, text = "Tick all the actions which the piece has").grid (row =1,
column = 0,
sticky = W)
self.Actions = BooleanVar()
self.Actions.set(None)
column = 0
row = 4
for action in Actions:
Checkbutton(self,text = action, variable = self.checkButton, value = action).grid( row = row,
column = column,
sticky = W)
if column == 5:
row +=1
column = 0
else:
column +=1
Button(self,text = "Add action", command = self.AddAction).grid(row = 1,
column = 0,
sticky = W)
self.action_entry = Entry(self)
self.action_entry.grid(row = 1, column = 1, sticky = W)
Button (self, text = "Create piece and it's actions", command = Add_to_dict).grid(row =2,
column = 0,
sticky = W)
self.Add_dict = Text(self, width =10, height = 2, wrap = WORD)
self.Add_dict.grid( row = 3, column = 0, columnspan = 4)
This function should add to the Actions list
def addAction(self):
action = self.action_entry.get()
Actions.append(action)
This function should just print the name of the piece and the actions chosen for it
def Add_to_dict(self):
actions = Actions.get()
piece = piece_entry.get()
rules = piece, ":", actions
self.Add_dict.delete(0.0,END)
self.Add_dict.insert(0.0,rules)
You need to use __init__, not __init
Also, if you're using BooleanVar, you must set it to True or False, not None:
self.Actions.set(False)
Also, you have a method named addAction but you are calling it like self.AddAction, and you have a method named Add_to_dict that you are calling like Add_to_dict rather than self.Add_to_dict.
And finally, you don't appear to be creating an instance of GameCreation anywhere.

Python Tkinter replacing the label and command of a button by clicking another button

I'm writing a scientific calculator with 2nd button. What is the function for second button so for example it changes sin to sin^-1, plus changing the sin button command; and if you click the 2nd button again, it changes sin^-1 back to sin
I would split my calculator up into section using different frames (one to show the calculations , one with buttons which won't have 2 functions and lastly the buttons which have 2 functions).
The reason I would split it up is because I would use destroying objects and making the new ones this splitting up means you can destroy the wanted frame rather than specific buttons (would require less code). Also for this I would have 3 create GUI defs. one would be the buttons with one function and the bit showing the calculations. one be the buttons which have 2 functions (their first function) and lastly the buttons which have 2 functions (their second functions). To decide between which GUI gen def to use have an if statement with global variable which gets changed each time 2nd function button called and that decides which def to use.
If it was just commands you wanted changing instead of both labels and commands I would have a variable which is etheir 1 or 2(change when 2nd button clicked) then in your definitions (ones which your buttons call) have an if statement to decide between to do normal action (e.g cos) or 2nd action (e.g cos-1).
Here is code below that uses what i have described in the first paragraph:
from tkinter import *
class Calc(Frame):
def __init__(self, master):
self.modefunction = 1
""" Initialize the frame. """
super(Calc,self).__init__(master)
self.grid()
self.calculations_frm = Frame(self, width=100, height=30)#bg = "red"
self.calculations_frm.grid(row = 0, column = 0, columnspan=2)
self.buttons_frm = Frame(self, width= 50, height=30,)#bg = "green")
self.buttons_frm.grid(row = 1, column = 1)
self.buttons_2_functions_1_frm = Frame(self, width=50, height=30)#bg = "blue")
self.buttons_2_functions_1_frm.grid(row = 1, column = 0)
self.create_GUI()
def create_show_calculations(self):
self.calculation_lbl = Label(self.calculations_frm, text = "will show caluclations here").pack()
def create_buttons(self):
#mc stands for mode change
self.mode_change_btn = Button(self.buttons_frm, text = "mc", command = self.mode_change, height = 1, width = 5)
self.mode_change_btn.grid(row = 0,column = 0)
self.plus_btn = Button(self.buttons_frm, text = "plus", height = 1, width = 5)
self.plus_btn.grid(row = 1,column = 0)
def create_GUI(self):
self.create_show_calculations()
self.create_buttons()
self.create_1_function_gui()
def create_1_function_gui(self):
self.tan_btn = Button(self.buttons_2_functions_1_frm, text = "tan", height = 1, width = 5)
self.tan_btn.grid(row = 0,column = 0)
self.san_btn = Button(self.buttons_2_functions_1_frm, text = "san", height = 1, width = 5)
self.san_btn.grid(row = 0,column = 1)
self.coz_btn = Button(self.buttons_2_functions_1_frm, text = "coz", height = 1, width = 5)
self.coz_btn.grid(row = 1,column = 0)
def create_2_function_gui(self):
self.buttons_2_functions_2_frm = Frame(self, width=50, height=30)#bg = "blue")
self.buttons_2_functions_2_frm.grid(row = 1, column = 0)
self.inverse_tan_btn = Button(self.buttons_2_functions_2_frm, text = "tan-1", height = 1, width = 5)
self.inverse_tan_btn.grid(row = 0,column = 0)
self.inverse_san_btn = Button(self.buttons_2_functions_2_frm, text = "san-1", height = 1, width = 5)
self.inverse_san_btn.grid(row = 0,column = 1)
self.inverse_coz_btn = Button(self.buttons_2_functions_2_frm, text = "coz-1", height = 1, width = 5)
self.inverse_coz_btn.grid(row = 1,column = 0)
def mode_change(self):
if self.modefunction == 1:
self.buttons_2_functions_1_frm.destroy()
self.modefunction = 2
self.buttons_2_functions_2_frm = Frame(self, width=50, height=30)#bg = "blue")
self.buttons_2_functions_2_frm.grid(row = 1, column = 0)
self.create_2_function_gui()
else:
self.buttons_2_functions_2_frm.destroy()
self.modefunction = 1
self.buttons_2_functions_1_frm = Frame(self, width=50, height=30)#bg = "blue")
self.buttons_2_functions_1_frm.grid(row = 1, column = 0)
self.create_1_function_gui()
root = Tk()
root.title("booking system")
root.geometry("500x500")
root.configure(bg="white")
app = Calc(root)
root.mainloop()

Categories

Resources