Tkinter frames not expanding equally with similar packing arguments - python-3.x

I'm trying to build a UI using tkinter that splits the window into 2 sections using 2 frames, however I can't get them to be equal in width.
Rough visualisation of layout:
My original approach was to have 1 frame with side = LEFT and 1 frame with side = RIGHT, and both frames would have fill = BOTH and expand = YES. This method worked fine when the frames were empty, but the left frame became wider than the right when widgets were added to them.
Left frame is red and right frame is blue, behaves as expected:
No longer works (same packing):
Code used (without menubar):
from tkinter import *
master = Tk()
master.geometry("500x500")
lframe = Frame(bg = 'red', relief = "raised", bd = 2)
rframe = Frame(bg = 'blue',relief = "raised", bd = 2)
# Set up for left frame
password_options = Listbox(lframe)
password_options.pack(side = "left", fill = 'both', expand = 1)
password_scrollbar = Scrollbar(lframe)
password_scrollbar.pack(side = 'left', anchor = 'w', fill = 'y', expand = 1)
# Set up for right frame
b = Button(rframe, text = "TEST")
b.pack(expand = 1, fill = 'both')
lframe.pack(side = "left", fill = "both", expand = 1)
rframe.pack(side = "right", fill = "both", expand = 1)
master.mainloop()
I thought the frames would still be equal in width after adding widgets, but it seems not. What could be causing this behaviour?

Related

Attaching a scrollbar to a listbox

I know that there have been some other questions about this, but was hoping to get some help with my current frame configuration as seen below in the code snippet. I have also attached some images, first is with no scrollbar set up. Second is when I uncommented out my scrollbar code.
Frame setup:
# -- Top Frame -- #
self.top = Frame(master, height = 71, bg = self.topColor)
self.top.pack(fill = X)
self.bottom = Frame(master, height = 650, bg = self.bottomColor)
self.bottom.pack(fill = X)
Listbox setup:
# myscroll = Scrollbar(self.bottom, orient = VERTICAL)
Label(self.bottom, text = 'Files Chosen:', bg = self.bottomColor).place(x = 4, y = 110)
self.qListBox = Listbox(self.bottom, width = 30, selectmode = SINGLE) # I did have yscrollcommand = myscroll
# myscroll.config(command = self.qListBox.yview)
# myscroll.pack(side = RIGHT, fill = Y)
self.qListBox.place(x = 4, y = 130)
Label(self.bottom, text = 'Deployment Queue:', bg = self.bottomColor).place(x = 360, y = 110)
self.dListBox = Listbox(self.bottom, width = 30, selectmode = MULTIPLE)
self.dListBox.place(x = 360, y = 130)
Figured out how to resolve this. Created three frames that are inside of my master frame as seen below:
my_frame = Frame(self.master)
my_secondF = Frame(self.master)
my_thirdF = Frame(self.master)
Once I did this I simply put my Lisboxes inside of those frames and placed them accordingly and configured my scrollbars
self.qListBox = Listbox(my_frame, yscrollcommand=myscroll_bar, width = 32, selectmode = SINGLE)
I still, however, appreciate all the replies :)

How to separate 2 label far apart with tkinter Python?

I would like to separate 2 elements far apart in Tkinter.
I have tried using column such that label_1 is column = 0, row = 0 and label 2 is column 19 and label 3 is column 20 but this still results in them being side by side in the middle. I have set my frame with pack(side =TOP).
I also tried using pack on my label such that label 2 & 3 are right and label 1 is left but still ended up with an unexpected result.
Hence is there a way to separate the 2 elements far apart?
Example
First I use a frame using pack() to display.
self.frameTop.pack(fill=BOTH, expand=NO)
Apparently the condition expand and fill played an important role in display the expected result.
The bottom shows the code for the layout of each element and its anchoring
# Monty Logo
self.icon = Label(self.frameTop, image = self.iconImage, font=('Zilla Slab', 16, 'bold'), borderwidth = 0, highlightthickness = 0, bg="#FFFFFF")
self.icon.pack(padx = 8, pady = 8, side = LEFT, anchor=NW)
# Use a canvas line to deine the cutting
self.labelSeperator = Separator(self.window, orient="horizontal")#Label(self.frameTop, bg="#000000", height= 2, width = int(ws)).pack(side= BOTTOM)
self.labelTitle_time = Label(self.frameTop, font=('Zilla Slab', 16), anchor = "w", borderwidth = 0, highlightthickness = 0, bg="#FFFFFF")
self.labelTitle_time.pack(padx=8,side = RIGHT, anchor=CENTER)
self.labelTitle_day = Label(self.frameTop, font=('Zilla Slab', 16, 'bold'), borderwidth = 0, highlightthickness = 0,bg="#FFFFFF")
self.labelTitle_day.pack(side = RIGHT, anchor=CENTER)

Function traceback says not receiving positional arguments

Issue begins at def displayAnswer
import tkinter as tk
#icftk stands for incompressible-flow toolkit
"""This program is being built to aid in getting all
parameters of a flow given certain initial conditions"""
#CREATES THE WINDOW
root = tk.Tk()
root.title("Incompressible Fluid Toolkit")
class flow:
"""This class contains all necessary parameters needed to define a flow that i
incompressible, 1D-Steady, idiabatic and encounters no energy gain or loss"""
def __init__(self,vel,diameter,density,viscosity,massflow = 0, Re = 0, newdia = 1, jetforce = 0, newvel = 0):
"""initialize a fluid with given basic measured properties"""
self.vel = vel
self.diameter = diameter
self.density = density
self.viscosity = viscosity
self.massflow = massflow # mass flow rate
self.Re = Re #Reynolds Number
self.newdia = newdia # downstream diameter for velocity change
self.jetforce = jetforce # force the stream can produce normal to a surface
self.newvel = newvel # new velocity after a cross sectional area change
def reynolds(self):
"""This function calculates reynolds
Pass ro, v, D and mu in the same unit system, in that order"""
self.Re = (self.diameter*self.vel*self.density)/(self.viscosity)
print(f"The Reynolds number for this flow is {self.Re}")
def mdot(self):
"""This function finds the mass flowrate of a flow"""
flowarea = 3.14159*(self.diameter**2) / 4
self.massflow = self.density*self.vel*flowarea
print(f"The mass flowrate is {self.massflow}")
def streamforce(self):
"""This function gives the max force that the fluid jet can apply
normal to a surface perpendicular to the flow"""
self.jetforce = self.massflow*self.vel
print(f"The maximum force the jet can apply is {self.jetforce}")
def velchange(self):
"""This function is used to determine the velocity change of
a flow when there is a change in cross sectional area of the pipe"""
newarea = 3.14159 * (self.newdia**2) / 4
self.newvel = self.massflow/(self.density*newarea)
print(f"At the location of the area change, there is a velocity change from {self.vel} to {self.newvel}")
#ALL ABOVE FUNCTIONS HAVE BEEN CONFIRMED TO WORK WITH GIVEN TEST CONDITIONS BELOW
#use test case velocity = 18.64, diameter = 0.017, density = 1.23, and viscosity = 0.0000184
#Display Entry Boxes
velo = tk.Label(root, text="Flow Velocity") # Create a text label
velo.grid(row = 0, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
veloent = tk.Entry()
veloent.grid(row = 0, column = 1, pady = 10)
diam = tk.Label(root, text="Pipe Diameter") # Create a text label
diam.grid(row = 1, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
diament = tk.Entry()
diament.grid(row = 1, column = 1, pady = 10)
dens = tk.Label(root, text="Fluid Density") # Create a text label
dens.grid(row = 2, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
densent = tk.Entry()
densent.grid(row = 2, column = 1, pady = 10)
visc = tk.Label(root, text="Fluid Viscosity") # Create a text label
visc.grid(row = 3, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element
viscent = tk.Entry()
viscent.grid(row = 3, column = 1, pady = 10)
#Display answers at the bottom of the window
def displayAnswer(veloent,diament,densent,viscent):
ve = float(veloent)#gets velocity entry and turns it into a float
di = float(diament)#gets diameter entry and turns it into a float
de = float(densent)#gets density entry and turns it into a float
vi = float(viscent)#gets viscosity entry and turns it into a float
fluid = flow(ve,di,de,vi)
fluid.reynolds()
fluid.mdot()
fluid.streamforce()
reynoldsanswer = tk.Label(root, text = "f{fluid.reynolds}")
reynoldsanswer.grid(row = 5)
mdotanswer = tk.Label(root, text = "f{fluid.mdot}")
mdotanswer.grid(row = 6)
streamforceanswer = tk.Label(root, text = "f{fluid.streamforce}")
streamforceanswer.grid(row = 7)
calculatebutton = tk.Button(root,command = displayAnswer)
calculatebutton.grid(row = 4)
root.mainloop()
I am new to tkinter, trying to get experience designing simple GUI. I am using a button to initiate a calculation to obtain values about an incompressible flow. When the button is pressed, the console throws this error.
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: displayAnswer() missing 4 required positional arguments: 'veloent', 'diament', 'densent', and 'viscent'
Similarly, if I try to convert the Entry into a float from a string outside of the function, the console throws a cannot convert string to float error.
Honestly not sure if all of the code is even right, but I'll cross these bridges one at a time.
Any insight is appreciated.
Best,
T
The problem is that your function requires parameters, but never uses them. When you call the function from a button, the button will not by default pass any options. That's why you get missing 4 required positional arguments - the function requires four, the button passes zero.
Since your function is actually doing the right thing and fetching the values it needs, there's no need to pass in the parameters. The simple fix is to simply remove them from the definition of the function:
def displayAnswer():
ve = float(veloent)#gets velocity entry and turns it into a float
di = float(diament)#gets diameter entry and turns it into a float
de = float(densent)#gets density entry and turns it into a float
vi = float(viscent)#gets viscosity entry and turns it into a float
...

I have created an Entry Widget and placed it in the grid but it doesn't show

I'm creating a front end for a customer access and on one of my pages, the entry widget that I want to place next to the label doesn't show, I don't get any errors.
I've placed it on a grid and moved it around, I have made sure nothing else is in it's part of the grid
from tkinter import *
from tkinter import ttk
newCustomerPage = Tk()
newCustomerPage.geometry('400x172') #define the size of the window
newCustomerPage.overrideredirect(True) #removes the bar at the top of the window
windowWidth = newCustomerPage.winfo_reqwidth() #Place the window in the middle of the page
windowHeight = newCustomerPage.winfo_reqheight()
positionRight = int(newCustomerPage.winfo_screenwidth()/2.3 - windowWidth/2)
positionDown = int(newCustomerPage.winfo_screenheight()/3 - windowHeight/2)
newCustomerPage.geometry("+{}+{}".format(positionRight, positionDown))
print("New Customer Page set")
customerPageLabel = Label(newCustomerPage, text = "New Customer", font = ("Calibri", 25), padx = 110)#.pack(side = TOP, pady = 1)
customerPageLabel.grid(row = 0, column = 0)
newCustNameLabel = Label(newCustomerPage, text = 'Given Name:', font = ("Calibri", 12))#.pack(side = TOP, pady = 1)
newCustNameLabel.grid(row = 1, column = 0)
newCustNameEntry = Entry(newCustomerPage, width = 10)
newCustNameEntry.grid(row = 1, column = 1)
close = ttk.Button(newCustomerPage, text = 'Close', width = 20, command = newCustomerPage.destroy)#.pack(side = TOP, pady = 8)
close.grid(row = 2, column = 0)
The page opens and displays "New Customer" then directly below that in the middle of the page displays "Given Name:" then directly below that displays the "close" button which functions fine, I want the entry box to display to the right of 'newCustNameLabel' but it doesn't
From the docs:
padx: The amount specifies how much horizontal external padding to leave on each side of the slave(s), in screen units.
So by setting padx=100 in your grid column #0, you applied padding for 100px on both sides, which pushes the Entry widget off screen.
Instead of using padx, what you should do is to set the columnspan of customerPageLabel so it can span over more than 1 column:
customerPageLabel = Label(newCustomerPage, text = "New Customer", font = ("Calibri", 25))
customerPageLabel.grid(row = 0, column = 0, columnspan=2)
And if you want the columns to occupy all the remaining space, set a weight to the columns:
newCustomerPage.columnconfigure(0,weight=1)
newCustomerPage.columnconfigure(1,weight=1)

tkinter histogram prints downwards

I am trying to draw two histograms alongside one another using tkinter canvas. Everything sort of works ( looks extremely scruffy at the moment) but the histograms are drawn downwards. I have tried making the y0 value negative, but then nothing at all is drawn.
I am using two lists of numerical data, the first with 50 observations and the other with eleven observations, the scales are not the same, but it is the qualitative effect I want at the moment.
The offending code is as follows:
root = Tk()
canvas = Canvas(root, width=620, height=400, background = "salmon")
canvas.grid()
# draw x-axis lines
canvas.create_line(0,2, 500, 0, width = 2, fill = "firebrick")
canvas.create_line(505,2, 610, 0, width = 2, fill = "dark slate blue")
# draw histograms
for idx in range(len(main_counts[0])):
canvas.create_rectangle(idx*10, main_counts[0][idx], 10 +(idx*10), 0, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 40, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
for idx in range(len(star_counts[2])):
canvas.create_rectangle((505 + idx*10), star_counts[2][idx], (515 + (idx*10)), 0, fill = "gold", outline = "dark slate blue")
canvas.create_text(505 + idx*10 + 8, 120, text = idx + 1, font = ("Comic sans MS", 8) , fill = "dark slate blue")
root.mainloop()
I know that I am missing something quite simple and obvious to all of you, but I just can't see it or the way to make my y0 negative which will presumably solve the problem. I can also not see my x-axes, but that may be because they are occluded by the histogram bars.
Many thanks for your patience and help! Any other suggestions about formatting the graphs will be welcomed including suggestions of best font to use for small digit screen display
The system coordinates start in the upper-left corner so you should write something like:
main_counts =[[10, 20, 30]]
for idx in range(len(main_counts[0])):
canvas.create_rectangle(idx*10, 200 -main_counts[0][idx], 10 +(idx*10), 200, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 210, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
You can use enumerate for more readable code:
for idx, val in enumerate(main_counts[0]):
canvas.create_rectangle(idx*10, 200 -val, 10 +(idx*10), 200, fill = "medium sea green", outline = "firebrick")
canvas.create_text(idx*10 + 8, 210, text = idx + 1, font = ("Comic sans MS",8), fill = "firebrick")
In simple terms - Try to create the histogram from a point on the coordinate according to your value as your second argument in rectangle_crete() function, and then go upto the point where you want your base of the histogram should,which will me constant for all your histograms. Because Tkinter coordinates starts from (0,0) and goes from up to down.
An example code is here -
from Tkinter import *
from random import randint # for testing histogram
master = Tk()
w = Canvas(master, width=1000, height=500)
w.pack()
start_point = 70
padding_y = 450
width = 50
height = 450
list = []
for i in range(1,10):
list.append(randint(1,4))
for i in range(1,10):
end_point = start_point+width
w.create_rectangle(start_point, list[i-1]*100, end_point, height, fill="blue")
start_point += width+20
mainloop()

Resources