The comments in the offending NewLevel function provide some explanation.
fuelstats.txt is a file with one number in it 94.5
When I run this code the error is:
unsupported operand - for float to StringVar
this is for new_level.set(fuel_level - kmltr), because one is a float but I've tried everything I can think of to change the types, I've even tried a get() maybe in the wrong way I don't know.
##call the tkinter module first with all libraries
##call the ttk module next but without all libraries so that
##I can use different lib as I need to
##...without the ttk lib I need to explicitly call the ttk function
from tkinter import *
from tkinter import ttk
##import fuel level from file
file = 'fuelstats.txt'
stats = open("fuelstats.txt").readlines()
##assign fuellevel to variable
fuel_level = stats[-1]
fuel_level = float(fuel_level)
ratekm = 2.5
###defining the calculate function here because it needs to be
###referenced early in the code
def calculate(*args):
value = float(km.get())
kmltr.set(value / ratekm)
##call the next function
NewLevel()
def NewLevel(*args):
global new_level
##this function does not work, it throws an exception
## that I cannot subtract a float from a stringVar
## this is obvious but how do I define the types properly?
new_level.set(fuel_level - kmltr)
def km_left():
global reserves
reserves = fuel_level * 2.5
n = 2
reserves = '{:.{}f}'.format(reserves, n)
root = Tk() ##set up main window container
root.title("Calculate Fuel")## give it a title
##next set up a frame widget which holds all the content
mainframe = ttk.Frame(root, padding="30 13 20 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
##these two lines tell tk to resize with main window
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
##define variables
km = StringVar()
kmltr = StringVar()
reserves = StringVar()
new_level = StringVar()
##call to functions
km_left()
##next create the three main widgets - input field for km
km_entry = ttk.Entry(mainframe, width=7, textvariable=km)
km_entry.grid(column=2, row=3, sticky=(W,E))
##the result fields (as labels) and the calculate button
ttk.Label(mainframe, text=fuel_level).grid(column=2, row=1, sticky=W)
ttk.Label(mainframe, text=reserves).grid(column=2, row=2, sticky=W)
ttk.Label(mainframe, textvariable=kmltr).grid(column=2, row=4, sticky=W)
ttk.Label(mainframe, text=new_level).grid(column=2, row=5, sticky=W)
ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=5, row=6, sticky=W)
##below I create labels for the widgets and place them in a the grid
ttk.Label(mainframe, text="km").grid(column=3, row=2, sticky=W)
ttk.Label(mainframe, text="litres").grid(column=3, row=1, sticky=E)
ttk.Label(mainframe, text="litres").grid(column=3, row=4, sticky=E)
ttk.Label(mainframe, text="fuel level is: ").grid(column=1, row=1, sticky=W)
ttk.Label(mainframe, text="fuel reserves = ").grid(column=1, row=2, sticky=W)
ttk.Label(mainframe, text="enter km traveled ").grid(column=1, row=3, sticky=W)
ttk.Label(mainframe, text="fuel used is: ").grid(column=1, row=4, sticky=W)
ttk.Label(mainframe, text="new fuel level is: ").grid(column=1, row=5, sticky=W)
##below is a loop to put padding around each field and widget
for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)
km_entry.focus() ##focus of the first field
root.bind('<Return>', calculate)##and allow the enter key to act as the button
root.mainloop()
##This final line tells Tk to enter its event loop,
## which is needed to make everything run.
You need to get the value inside the StringVar and then convert that to a float.
new_level.set(fuel_level - float(kmltr.get()))
Related
i don't really undestand, why the "results" label is not updating when i click on the button.
If someone can help me to understand!
Thank You
from tkinter import *
from tkinter import ttk
def add_function():
results.config(n1.get() + n2.get())
root = Tk()
root.geometry("500x100") # Size of the window
root.title("Add Calculator") # Title of the window
main_frame = ttk.Frame(root, padding="3 3 12 12")
main_frame.grid(column=0, row=0, sticky=(N,S,E,W))
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
n1 = DoubleVar()
n1_entry = Entry(main_frame, width= 10, textvariable= n1)
n1_entry.grid(column=2, row=1, sticky=(N))
symbol_add = Label(main_frame, text="+")
symbol_add.grid(column=3, row=1, sticky=(N))
n2 = DoubleVar()
n2_entry = Entry(main_frame, width= 10, textvariable= n2)
n2_entry.grid(column=4, row=1, sticky=(N))
symbol_equal = Button(main_frame, width=10, text="=", command= add_function )
symbol_equal.grid(column=5, row=1, sticky=(N))
results = Label(main_frame, text=add_function(), background="#C0C0C0")
results.grid(column=6, row=1, sticky=(N))
root.mainloop()
I've tryed different variant, but it's either i have an error, or the label is printing a random number even before i modify the entry.
It is because you don't update the label. Returning something from a button command is pointless because the caller isn't your code. It's called from mainloop and mainloop doesn't know what to do with the return code.
To update the label you must call the configure method:
results.configure(text=n1.get() + n2.get())
I'm making a GUI with Tkinter (Tcl/Tk version 8.6.12) on Python 3.9. Up until now, I mostly had experience with pure Tk, but wanted to try Ttk out. My problem is that running:
someText = tk.StringVar(value="Some Text")
ttk.Label(mainframe, textvariable=someText).grid(column=0, row=0, sticky=(W, E))
does reserve some space for the string, but doesn't display it. When using the tk.Label class, it works like a charm...
What am I doing wrong, or is the ttk.Label class broken?
EDIT: here is my full MNWE (I wrapped everything in a function because I will need to integrate everything with some other code I have already written and this is the best way I've found to do it):
from tkinter import *
from tkinter import ttk
def generateGUI():
# Generates the main window
root = Tk()
root.title("Some interesting title")
root.resizable(FALSE, FALSE)
# Adds a frame for themed tk
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1) # setup the frame to scale with the window horizontally
root.rowconfigure(0, weight=1) # setup the frame to scale with the window vertically
# Creates a canvas
canv = Canvas(mainframe, bg="green")
canv.grid(column=0, row=0, sticky=(N, E, S, W))
# Sets some information panel up
info = ttk.Frame(mainframe)
info.grid(column=1, row=0, sticky=(N, E))
title = StringVar(value="<title>")
text1 = StringVar(value="<text1>")
text2 = StringVar(value="<text2>")
text3 = StringVar(value="<text3>")
# Adds the different labels
ttk.Label(info, font=25, textvariable=title).grid(column=0, row=0, sticky=N, columnspan=2)
ttk.Label(info, text="Info 1: ").grid(column=0, row=1, sticky=W)
ttk.Label(info, text="Info 2: ").grid(column=0, row=2, sticky=W)
ttk.Label(info, text="Info 3: ").grid(column=0, row=3, sticky=W)
ttk.Label(info, textvariable=text1).grid(column=1, row=1, sticky=E)
ttk.Label(info, textvariable=text2).grid(column=1, row=2, sticky=E)
ttk.Label(info, textvariable=text3).grid(column=1, row=3, sticky=E)
for child in mainframe.winfo_children():
child.grid_configure(padx=5, pady=5)
return root
gui = generateGUI()
gui.mainloop()
Your instance of StringVar is a local variable. Make it/them an instance variable if you're using classes, or a global variable if not.
def generateGUI():
global title
...
title = StringVar(value="<title>")
ttk.Label(info, font=25, textvariable=title)
This example code is working. You need to import ttk and change remove sticky.
why_no_sticky - good example on the link how grid works.
from tkinter import ttk
import tkinter as tk
mainframe = tk.Tk()
someText = tk.StringVar(value="Some Text")
label1 = ttk.Label(mainframe, textvariable=someText)
label1.grid(column=0, row=0)
mainframe.mainloop()
I am currently writing a simple piece of inventory management software for a school python assessment. My GUI involves listing out all of the items in stock, and giving the user the ability to stock/sell multiple items at once. This was achieved by placing an entry (amount to buy) and a scale widget (amount to sell) next to each item, and utilizing an "apply" button to enact the changes (For context, the scenario is a comic book store):
https://i.imgur.com/7cesQm5.png
This is the code that I have used to create the new widgets from each file (all of which are contained in a dictionary, pulled from a CSV file):
itteration = 2
labels = {}
nameTitle = Label(main, text="Title").grid(row=1, column=1, padx=5)
stockTitle = Label(main, text="Stock").grid(row=1, column=2, padx=5)
buyTitle = Label(main, text="Amount to Buy").grid(row=1, column=3, padx=5)
sellTitle = Label(main, text="Amount to Sell").grid(row=1, column=4, padx=5)
for item in comic_books:
name = item.replace(" ", "")
nameStock = "{}Stock".format(item).replace(" ", "")
nameBuy = "{}Buy".format(item).replace(" ", "")
nameSell = "{}Sell".format(item).replace(" ", "")
labels[name] = Label(main, text=item+":")
labels[name].grid(column=1, row=itteration, padx=5)
labels[nameStock] = Label(main, text=comic_books.get(item))
labels[nameStock].grid(column=2, row=itteration, padx=5)
labels[nameBuy] = Entry(main)
labels[nameBuy].grid(column=3, row=itteration, padx=20)
labels[nameSell] = Scale(main, from_=0, to=comic_books.get(item), orient=HORIZONTAL)
labels[nameSell].grid(column=4, row=itteration, padx=5)
itteration += 1
However, I am not a fan of how the the scales appear to be centering themselves in the row based on both the center of the slider, and the number on top. This makes the slider appear lower than the entry widget next to it.
https://i.imgur.com/9CQTvWS.png
My question is: how would I go about potentially offsetting the position of the scale widgets upwards by a given amount (maybe 5-10px?) from its original position, to push the actual slider in line with the entry widget before it?
Please let me know if you need any extra clarification
Cheers,
-Sean
Using sticky='s' and sticky='n' to stick to bottom (south) and top (north) of cell I got this
I didn't use different values in pady
import tkinter as tk
root = tk.Tk()
l = tk.Label(root, text='Label')
l.grid(column=1, row=0, padx=5, sticky='s', pady=10)
e = tk.Entry(root)
e.grid(column=2, row=0, padx=20, sticky='s', pady=10)
s = tk.Scale(root, from_=0, to=10, orient='horizontal')
s.grid(column=3, row=0, padx=5, sticky='n', pady=10)
root.mainloop()
Using sticky with pady=(0,20) for one widget I got this
import tkinter as tk
root = tk.Tk()
l = tk.Label(root, text='Label')
l.grid(column=1, row=0, padx=5, sticky='s', pady=10)
e = tk.Entry(root)
e.grid(column=2, row=0, padx=20, sticky='s', pady=10)
s = tk.Scale(root, from_=0, to=10, orient='horizontal')
s.grid(column=3, row=0, padx=5, sticky='n', pady=(0,20))
root.mainloop()
I have three frames, but when a new label appears the frames automatically readjust to a new size. How do I stop the readjustment and have the size of each frame set and immutable.
#Import tkinter to make gui
from tkinter import *
from tkinter import ttk
import codecs
#Program that results when user attempts to log in
def login(*args):
file = open("rot13.txt", "r")
lines = file.readlines()
uname = user.get()
pword = pw.get()
for i in lines:
x = i.split()
if codecs.encode(uname,'rot13') == x[0] and codecs.encode(pword,'rot13') == x[1]:
result.set("Successful")
break;
else:
result.set("Access Denied")
root = Tk()
root.title("Login")
#Configures column and row settings and sets padding
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe['borderwidth'] = 5
mainframe['relief'] = "solid"
mainframe.grid(column=1, row=1, columnspan=3, rowspan=2)
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
mainframe2 = ttk.Frame(root, padding="3 3 12 12")
mainframe2['borderwidth'] = 5
mainframe2['relief'] = "solid"
mainframe2.grid(column=1, row=3, columnspan=1, rowspan=3)
mainframe2.columnconfigure(0, weight=1)
mainframe2.rowconfigure(0, weight=1)
mainframe3 = ttk.Frame(root, padding="3 3 12 12")
mainframe3['borderwidth'] = 5
mainframe3['relief'] = "solid"
mainframe3.grid(column=2, row=5)
mainframe3.columnconfigure(0, weight=1)
mainframe3.rowconfigure(0, weight=1)
#anchors for widgets
user = StringVar()
pw = StringVar()
result = StringVar()
#Asks user input
user_entry = ttk.Entry(mainframe, width=20, textvariable=user)
user_entry.grid(column=2, row=1, sticky=(W, E))
pw_entry = ttk.Entry(mainframe, width=20, textvariable=pw)
pw_entry.grid(column=2, row=2, sticky=(W, E))
#Labels to make user-friendly and able to understand
ttk.Label(mainframe, text="Username ").grid(column=1, row=1, sticky=W)
ttk.Label(mainframe, text="Password ").grid(column=1, row=2, sticky=W)
ttk.Label(mainframe2, text="").grid(column=1, row=3, sticky=W)
ttk.Label(mainframe2, text="Result").grid(column=1, row=4, sticky=W)
ttk.Label(mainframe2, text="").grid(column=1, row=5, sticky=W)
#Button to log in
ttk.Button(mainframe3, text="Login", command=login).grid(column=3, row=5, sticky=(W,E))
#Makes a spot to put in result
ttk.Label(mainframe2, textvariable=result).grid(column=2, row=4, sticky=(W, E))
#Opens up with item selected and allows you to enter username without having to click it
user_entry.focus()
#Runs calculate if click enter
root.bind('<Return>', login)
root.mainloop()
Here is the before and after picture of the results:
As you can see the frames change sizes after the program runs its course. How do i stop this re-size and stick to a predetermined size?
The simplest solution in this specific case is to give the label with the text a fixed width. When you specify a fixed width for a label, tkinter will do it's best to honor that width (though a lot depends on how you place it on the screen with pack, place or grid).
ttk.Label(..., width=12).grid(...)
Another solution is to turn off geometry propagation, which means you're responsible for giving the frame an explicit width and height:
mainframe2 = ttk.Frame(..., width=200, height=100)
...
mainframe2.grid_propagate(False)
I do not recommend turning geometry propagation off. Tkinter is usually very good at computing the right size for widgets, and this usually results in very poor behavior if you change fonts, screen resolutions, or root window sizes.
Just add a width option to ttk.Label(mainframe2, textvariable=result).grid(column=2, row=4, sticky=(W, E)):
So it will become like this:
ttk.Label(mainframe2, textvariable=result, width=20).grid(column=2, row=4, sticky=(W, E))
i found this code online and i wanted to try it out because im trying to figure out how to have my label to change while i type things into my messagebox. I tried the getmethod but i have been struggling with using it. So i found this code and when i tried it i get the error that ttk is undefined but it clearly is.
from Tkinter import *
from ttk import *
def calculate(*args):
try:
value = float(feet.get())
meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0)
except ValueError:
pass
root = Tk()
root.title("Feet to Meters")
mainframe = ttk.Frame(root, padding="3 3 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
feet = StringVar()
meters = StringVar()
feet_entry = ttkEntry(mainframe, width=7, textvariable=feet)
feet_entry.grid(column=2, row=1, sticky=(W, E))
ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E))
ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=3, row=3, sticky=W)
ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W)
ttk.Label(mainframe, text="is equivalent to").grid(column=1, row=2, sticky=E)
ttk.Label(mainframe, text="meters").grid(column=3, row=2, sticky=W)
for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5)
feet_entry.focus()
root.bind('<Return>', calculate)
root.mainloop()
Traceback (most recent call last):
File "tk8.py", line 15, in
mainframe = ttk.Frame(root, padding="3 3 12 12")
NameError: name 'ttk' is not defined
So i found this code and when i tried it i get the error that ttk is
undefined but it clearly is.
You're star-importing from the module, though, using from ttk import *, so the name ttk doesn't refer to anything. For example, from math import * would bring sin, cos, etc., all into your namespace but the name math would still be undefined. The code works for me if I switch the imports to
from Tkinter import *
import ttk
and add the missing . from ttk.Entry in this line:
feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet)
You are looking for the trace_variable method. Here is a fixed version:
from Tkinter import Tk, StringVar
import ttk
def calculate(*args):
try:
value = float(feet.get())
meters.set('%g' % (0.3048 * value))
except ValueError:
if not feet.get():
meters.set('')
root = Tk()
root.title("Feet to Meters")
feet = StringVar()
feet.trace_variable('w', calculate)
meters = StringVar()
main = ttk.Frame(root)
main.grid(sticky='nsew')
ttk.Label(main, text="Feet:").grid(row=0, sticky='e')
feet_entry = ttk.Entry(main, width=7, textvariable=feet)
feet_entry.grid(row=0, column=1, sticky='ew')
feet_entry.focus()
ttk.Label(main, text="Meters:").grid(row=1, sticky='e')
ttk.Label(main, textvariable=meters).grid(row=1, column=1, sticky='ew')
root.mainloop()