Tkinter - How to limit the space used by a Text? - python

I'm trying to create a factorial calculator GUI.
The program works fine, but the problem I'm having is that when there are too many numbers coming in from the output, the screen automatically increases in width. I've tried using tk.Text to create a limit to the size of the textbox and so the text continues to the next row when the columns are filled.
But when I had to input text in to the tk.Text it didn't work since the variable I used is being processed in the function that gets called when the button is pressed. I have tried googling this problem but I couldn't find anything, I did find some people explaining how to use variables that get created/processed inside of a function, but that didn't work so I think I have done something wrong in my code.
Note: I am using lambda to call my function (not sure if this is important or not).
TLDR: Text gets too long when too much information is outputted. tk.Text didn't work for me since I couldn't figure out how to use the variable that is created/processed inside of a function that is only called when the button is pressed.
Here is my entire code: https://pastebin.com/1MkdRjVE
Code for my function:
def start_calc():
output_array = ["placehold"]
start_text.set("Loading...")
i = 1
global e1
global e2
output_array.clear()
string = e1.get()
string2 = e2.get()
integr = int(string)
integr2 = int(string2)
if string == "":
error_message.set("Please enter correct numbers.")
elif string2 == "":
error_message.set("Please enter correct numbers.")
else:
while integr2 >= i:
calc = integr ** i
calcstr = (str(calc))
output_array.append(calcstr)
i += 1
start_text.set("Start!")
output_array_str = (', '.join(output_array))
output_msg.set("Output: " + output_array_str)
print(output_array_str) #This is just so I know if it's working or not in the terminal
Code for my output:
output_msg = tk.StringVar()
output_text = tk.Label(root, textvariable=output_msg, font="Raleway")
output_msg.set("Output: ")
output_text.grid(columnspan=3, column=0, row=14)

I think this is what you are looking for:
#Imports
import tkinter as tk
#Variables
root = tk.Tk()
#Tkinter GUI setup basic
canvas = tk.Canvas(root, width= 400, height=400)
canvas.grid(columnspan=3, rowspan=120)
#Title
text = tk.Label(root, text="Calculating factorials", font="Raleway")
text.grid(column=1, row=1)
#Function
def start_calc():
output_array = ["", ""]
start_text.set("Loading...")
i = 1
global e1
global e2
output_array.clear()
string = e1.get()
string2 = e2.get()
integr = int(string)
integr2 = int(string2)
if string == "":
error_message.set("Please enter correct numbers.")
elif string2 == "":
error_message.set("Please enter correct numbers.")
else:
while integr2 >= i:
calc = integr ** i
calcstr = (str(calc))
output_array.append(calcstr)
i += 1
start_text.set("Start!")
output_array_str = (', '.join(output_array))
# Change the output
output_text.config(state="normal")
# delete last output:
output_text.delete("0.0", "end")
# insert new output:
output_text.insert("end", output_array_str)
output_text.config(state="disabled")
print(output_array_str) #This is just so I know if it's working or not in the terminal
#input
tk.Label(root, text="Number :").grid(row=10)
tk.Label(root, text="Factorial :").grid(row=11)
e1 = tk.Entry(root)
e2 = tk.Entry(root)
e1.grid(row=10, column=1)
e2.grid(row=11, column=1)
#Error message if the input is invalid
error_message = tk.StringVar()
error_text = tk.Label(root, textvariable=error_message, font="Raleway")
error_message.set(" ")
error_text.grid(column=1, row=12)
#Startbutton
start_text = tk.StringVar()
start_btn = tk.Button(root, textvariable=start_text, command=start_calc, font="Raleway", bg="#20bebe", fg="white", height=2, width=15)
start_text.set("Start!")
start_btn.grid(column=1, row=13, pady=10)
#output
output_text = tk.Text(root, height=1, width=20, wrap="none", font="Raleway")
output_text.insert("end", "Output")
output_text.config(state="disabled")
output_text.grid(columnspan=3, column=0, row=14, sticky="news")
#Adding a scrollbar
scrollbar = tk.Scrollbar(root, orient="horizontal", command=output_text.xview)
scrollbar.grid(columnspan=3, column=0, row=15, sticky="news")
output_text.config(xscrollcommand=scrollbar.set)
#disclaimer message
disclaimer_text = tk.Label(root, text="Disclaimer: The factorials will be printed from 1 to the number you entered.")
disclaimer_text.grid(columnspan=3, column=0, row=110)
root.mainloop()
I used a <tkinter.Text> widget with wrap="none", height=1 and width=20 to make the output box. I disabled the entry so that the user can't change the results but can still copy it.

Related

python gui AND/OR programm

I am using python GUI. So here's the deal https://learn.digilentinc.com/Documents/Digital/BT02_03_Basic_Logic_Truth_Table/AndOrTruthTable.svg
It should be like this. For example, if the user writes 1 in entry a and 0 in entry b and he chooses "and" radio button in label c should appear 0.
I tried something but it didn't work. Can anyone help me?
This is my code that doesn't work:
from tkinter import *
root = Tk()
a = Label(root,text = "enter number a")
b = Label(root,text = "enter number b")
c = Label(root)
a.grid(row=0, sticky=E)
b.grid(row=1, sticky=E)
c.grid(row=3, sticky=E)
entry_a = Entry(root)
entry_b = Entry(root)
entry_a.grid(row=0, column=1)
entry_b.grid(row=1,column=1)
v = IntVar()
def ifvisnull(event):
if entry_a == 1 and entry_b == 1:
print(1)
else:
print(0)
def ifvisone(event):
if entry_a == 0 and entry_b == 0:
print(0)
else:
print(1)
button1 = Radiobutton(root, text="and", variable = v, value = True, command = ifvisnull())
button2 = Radiobutton(root, text="or", variable = v, value = False, command = ifvisone())
button1.grid(row=2,column=0)
button2.grid(row=2,column=1)
root.mainloop()
my name is Vikas.
So there are a few problems i your code.
in all radiobutton , in the command option you had put parenthesis-()
infront of the command which you have to remove
ex: command=ifvisnull
then in all functions you have given event argument which you need not to
until and unless you are binding the widget to an event.
So,just remove event argument
Lastly , in each function to get the value of entry box you need to use
entry.get() function/method.
Directly writing entry_a will not give you the value
And also this method will return in str type .

How do I pass arguments and get return values from a tkinter button press function?

I am trying to get the word entered in the field(exp_textbox) to be passed as an arguement to the function(definition).
This is the code below for the dictionary app.
the json file is a python dictionary of words and their meaning. Is there a way i could do this?
import tkinter
import json
import difflib
data = json.load(open('data.json'))
main_window = tkinter.Tk()
def definition(w):
w = w.lower()
match_word = difflib.get_close_matches(w, data.keys())
if w in data:
result = data[w]
return result
elif len(match_word) > 0:
answer = input(f"Did you mean '{match_word[0]}'? [Y/N]").upper()
if answer == 'Y':
result = data[match_word[0]]
return result
elif answer == 'N':
return f"'{w}' is not in my dictionary"
else:
return 'command not understood'
else:
return f"'{w}' is not in my dictionary"
# Window Title
main_window.title('Dictionary')
main_window.geometry('320x320')
# Expression Frame
exp_frame = tkinter.Frame(main_window)
exp_frame.grid(row=0, column=0, columnspan=3, sticky='new')
exp_label = tkinter.Label(exp_frame, text='Enter your Word Here: ')
exp_label.grid(row=0, column=0)
The word provided in the field below should be passed to the function(definition) as an arguement.
exp_textbox = tkinter.Entry(master=exp_frame)
exp_textbox.grid(row=0, column=1)
exp_label = tkinter.Button(exp_frame, text='Search', command=definition(exp_textbox.get()))
exp_label.grid(row=0, column=3)
# Definition Frame
def_frame = tkinter.Frame(main_window)
def_frame.grid(row=2, column=0, columnspan=3, sticky='new')
def_label = tkinter.Label(def_frame, text='Definition is : ')
def_label.grid(row=2, column=0)
The return value of the function would then be returned here for the user.
def_textbox = tkinter.StringVar()
tkinter.Entry(def_frame, textvariable=def_textbox).grid(row=2, column=1)
main_window.mainloop()
So there are a couple of problems evident.
Running the function on button press
The first issue is that when you are creating the button you immediately run the definition function, instead of only running it when the button is pressed. How to pass arguments to a Button command in Tkinter? has some solutions to this issue, but a simple addition would be:
exp_label = tkinter.Button(exp_frame, text='Search', command=lambda: definition(exp_textbox.get()))
Getting the result of running the function
The next issue is that you are returning the result of checking the definition to the point of call (to the Button object) which does not know what to do with this. A quick way to work around this would be to set the value in the textbox directly in the function itself:
Move the definition of your def_textbox Stringvar above the button
Pass the def_textbox to your button command
Instead of returning the result, use set to change the textbox
Giving final code like:
import tkinter
import json
import difflib
data = json.load(open('data.json'))
main_window = tkinter.Tk()
def definition(w, txtbox):
print(f'In definition with {w}')
w = w.lower()
match_word = difflib.get_close_matches(w, data.keys())
if w in data:
result = data[w]
txtbox.set(result)
elif len(match_word) > 0:
answer = input(f"Did you mean '{match_word[0]}'? [Y/N]").upper()
if answer == 'Y':
result = data[match_word[0]]
txtbox.set(result)
elif answer == 'N':
txtbox.set(f"'{w}' is not in my dictionary")
else:
txtbox.set('command not understood')
else:
txtbox.set(f"'{w}' is not in my dictionary")
# Window Title
main_window.title('Dictionary')
main_window.geometry('320x320')
def_textbox = tkinter.StringVar()
# Expression Frame
exp_frame = tkinter.Frame(main_window)
exp_frame.grid(row=0, column=0, columnspan=3, sticky='new')
exp_label = tkinter.Label(exp_frame, text='Enter your Word Here: ')
exp_label.grid(row=0, column=0)
exp_textbox = tkinter.Entry(master=exp_frame)
exp_textbox.grid(row=0, column=1)
exp_label = tkinter.Button(exp_frame, text='Search', command=lambda: definition(exp_textbox.get(), def_textbox))
exp_label.grid(row=0, column=3)
# Definition Frame
def_frame = tkinter.Frame(main_window)
def_frame.grid(row=2, column=0, columnspan=3, sticky='new')
def_label = tkinter.Label(def_frame, text='Definition is : ')
def_label.grid(row=2, column=0)
tkinter.Entry(def_frame, textvariable=def_textbox).grid(row=2, column=1)
main_window.mainloop()

How to correct the output from an entry box

When using the feeder button, the script for F runs through entirely through to the print before the 'master' box appears, then does not react to the inputs from the 'master' box. This results in the output being 0.0 kW because the input is a long decimals followed by an L, when what I, the user inputs is 8777
I have been roaming the internet for about a day now with no luck finding anything. I am very new to TK but have been trying to learn it.
def F():
master = tk.Tk()
tk.Label(master, text = 'Feeder Number: ').grid(row=0)
entry1 = tk.Entry(master)
entry1.grid(row=0, column=1)
button2 = tk.Button(master,
text=' Confirm',
command=entry1.get())
button2.pack()
button2.grid(row=0, column=2)
fn = entry1.pack()
print fn
feed = filtered['Feeder']==fn
feedfn = filtered[feed]
Cap = feedfn['AC Name Plate Capacity <= 10kw']
Cap = Cap.astype(float)
AcPv = Cap.sum()
print 'The total PV on this feeder is:', AcPv, 'kW'
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame,
text='Exit',
fg='red',
command=quit)
button.pack()
button.grid(row=1, column=1)
Fee = tk.Button(frame,
text='Feeder',
command=F)
Fee.pack()
Fee.grid(row=0, column=1)
root.mainloop()
Expected 27.702
Output 0.0
Given that I will not be posting the csv,
entry1/fn should be 8777
currently 'none'
UPDATE
I am now receiving an output of PY_VAR when printing fn, I understand that the code is running all the way through before taking an input. Any recommendations for how to take the input before the filters are run?
def F():
master = tk.Tk()
tk.Label(master, text = 'Feeder Number: ').grid(row=0)
entry1 = tk.Entry(master)
entry1.grid(row=0, column=1)
button2 = tk.Button(master,
text=' Confirm',
command=entry1.get())
button2.grid(row=0, column=2)
fn = tk.IntVar()
print fn
feed = filtered['Feeder']==fn
feedfn = filtered[feed]
Cap = feedfn['AC Name Plate Capacity <= 10kw']
Cap = Cap.astype(float)
AcPv = Cap.sum()
print 'The total PV on this feeder is:', AcPv, 'kW'
For those interested in the final code (Which worked for me):
def F():
master = tk.Tk()
tk.Label(master, text = 'Feeder Number: ').grid(row=0)
entry = tk.Entry(master)
entry.grid(row=0, column=1)
def pint():
data = entry.get()
master.destroy()
feed = filtered['Feeder']==data
feedfn = filtered[feed]
Cap = feedfn['AC Name Plate Capacity <= 10kw']
Cap = Cap.astype(float)
AcPv = Cap.sum()
fdf = tk.Tk()
tk.Label(fdf, text = AcPv).grid(row=0)
button4 = tk.Button(fdf,
text = ' Exit',
fg='red',
command=fdf.destroy)
button4.grid(row=1)
button2 = tk.Button(master,
text=' Confirm',
command = pint)
button2.grid(row=0, column=2)
button3 = tk.Button(master,
text = ' Exit',
fg='red',
command=master.destroy)
button3.grid(row=0, column=3)
master.mainloop()
There a few mistake in your code that lead to the different output you have received.
First, why is your code executing without showing the master box :
Your tkinter need a mainloop() call if you want a persistent window.
master.mainloop()
You did that right with your root, but your master lacks that mainloop. This line is what basically keeping your GUI alive and looping over it for changes until it is destroyed one way or another. You need to add this line after creating your widgets in order to be able to interact with the window. Anything written after this line (but still indented in the definition) will be executed when your window is closed, either manually or with the command :
master.destroy()
Next, although this will yield a working window, you can still interact with your root window while the master window is up, which can lead to problems if you are expecting variable from master. I suggest you read about the Toplevel widget which is made specifically for cases like yours. (http://effbot.org/tkinterbook/toplevel.htm) Alternatively, you could also use tkinter's tkSimpleDialog.askinteger or askfloat functions which seems perfect for your application.
Finally, the entry allows you to write text but how to access the text? you can use Entry1.get() to extract the content of the entry, or as you have started to to in your update, you can assign a tkinter variable to the entry. This variable will be updated as you change write strings or numbers in the entry. To bind the variable to your entry, you must state it in the entry's creation :
fn = tk.StringVar(value = '000')
entry1 = tk.Entry(master, textvariable = fn)
*Note, this will require your fn variable to be initialized before the entry. Also, you can initialize a value for that variable upon creation
the tkinter variable is an object which is why when you print it, you get PY_VAR. (the type of object) To access the value, you need to use the get() method :
print(fn.get())

Get input in Python tkinter Entry when Button pressed

I am trying to make a 'guess the number' game with Pyhon tkinter but so far I have not been able to retrieve the input from the user.
How can I get the input in entry when b1 is pressed?
I also want to display a lower or higher message as a clue to the player but I am not sure if what I have is right:
import time
import random
import decimal
import tkinter as tk
root = tk.Tk()
randomnum = float(decimal.Decimal(random.randrange(100,10000))/100)
guess = 0
def get(entry):
guess = entry.get()
return guess
def main():
b1 = tk.Button(root, text="Guess", command=get)
entry = tk.Entry()
b1.grid(column=1, row=0)
entry.grid(column=0, row=0)
root.mainloop()
print(guess)
if guess < randomnum:
l2 = tk.Label(root, text="Higher!")
l2.grid(column=0, row=2)
elif guess > randomnum:
l3 = tk.Label(root, text="Lower!")
l3.grid(column=0, row=2)
while guess != randomnum:
main()
l4 = tk.Label(root, text="Well guessed")
time.sleep(10)
You could define get inside main, so that you can access the entry widget you created beforehand, like this:
entry = tk.Entry()
def get():
guess = entry.get()
return guess # Replace this with the actual processing.
b1 = tk.Button(root, text="Guess", command=get)
You've assembled random lines of code out of order. For example, the root.mainloop() should only be called once after setting up the code but you're calling it in the middle of main() such that anything after won't execute until Tk is torn down. And the while guess != randomnum: loop has no place in event-driven code. And this, whatever it is, really should be preceded by a comment:
randomnum = float(decimal.Decimal(random.randrange(100,10000))/100)
Let's take a simpler, cleaner approach. Rather than holding onto pointers to the the various widgets, let's use their textvariable and command properties to run the show and ignore the widgets once setup. We'll use StringVar and IntVar to handle input and output. And instead of using sleep() which throws off our events, we'll use the after() feature:
import tkinter as tk
from random import randint
def get():
number = guess.get()
if number < random_number:
hint.set("Higher!")
root.after(1000, clear_hint)
elif number > random_number:
hint.set("Lower!")
root.after(1000, clear_hint)
else:
hint.set("Well guessed!")
root.after(5000, setup)
def setup():
global random_number
random_number = randint(1, 100)
guess.set(0)
hint.set("Start Guessing!")
root.after(2000, clear_hint)
def clear_hint():
hint.set("")
root = tk.Tk()
hint = tk.StringVar()
guess = tk.IntVar()
random_number = 0
tk.Entry(textvariable=guess).grid(column=0, row=0)
tk.Button(root, text="Guess", command=get).grid(column=1, row=0)
tk.Label(root, textvariable=hint).grid(column=0, row=1)
setup()
root.mainloop()
Here is a tkinter version on the number guessing game.
while or after are not used!
Program checks for illegal input (empty str or words) and reports error message. It also keeps track of the number of tries required to guess the number and reports success with a big red banner.
I've given more meaningful names to widgets and used pack manager instead of grid.
You can use the button to enter your guess or simply press Return key.
import time
import random
import tkinter as tk
root = tk.Tk()
root.title( "The Number Guessing Game" )
count = guess = 0
label = tk.Label(root, text = "The Number Guessing Game", font = "Helvetica 20 italic")
label.pack(fill = tk.BOTH, expand = True)
def pick_number():
global randomnum
label.config( text = "I am tkinking of a Number", fg = "black" )
randomnum = random.choice( range( 10000 ) )/100
entry.focus_force()
def main_game(guess):
global count
count = count + 1
entry.delete("0", "end")
if guess < randomnum:
label[ "text" ] = "Higher!"
elif guess > randomnum:
label[ "text" ] = "Lower!"
else:
label.config( text = f"CORRECT! You got it in {count} tries", fg = "red" )
root.update()
time.sleep( 4 )
pick_number()
count = 0
def get( ev = None ):
guess = entry.get()
if len( guess ) > 0 and guess.lower() == guess.upper():
guess = float( guess )
main_game( guess )
else:
label[ "text" ] = "MUST be A NUMBER"
entry.delete("0", "end")
entry = tk.Entry(root, font = "Helvetica 15 normal")
entry.pack(fill = tk.BOTH, expand = True)
entry.bind("<Return>", get)
b1 = tk.Button(root, text = "Guess", command = get)
b1.pack(fill = tk.BOTH, expand = True)
pick_number()
root.geometry( "470x110" )
root.minsize( 470, 110 )
root.mainloop()
Correct way to write guess number.
I write a small script for number guessing game in Python in
get_number() function.
Used one widget to update Label instead of duplicating.
I added some extra for number of turns that you entered.
Code modified:
import time
import random
import decimal
import tkinter as tk
root = tk.Tk()
randomnum = float(decimal.Decimal(random.randrange(100,10000))/100)
print(randomnum)
WIN = False
GUESS = 0
TURNS = 0
Vars = tk.StringVar(root)
def get_number():
global TURNS
while WIN == False:
Your_guess = entry.get()
if randomnum == float(Your_guess):
guess_message = f"You won!"
l3.configure(text=guess_message)
number_of_turns = f"Number of turns you have used: {TURNS}"
l4.configure(text=number_of_turns)
l4.grid(column=0, row=3, columnspan=3, pady=5)
WIN == True
break
else:
if randomnum > float(Your_guess):
guess_message = f"Your Guess was low, Please enter a higher number"
else:
guess_message = f"your guess was high, please enter a lower number"
l3.configure(text=guess_message)
l3.grid(column=0, row=2, columnspan=3, pady=5)
TURNS +=1
return Your_guess
label = tk.Label(root, text="The Number Guessing Game", font="Helvetica 12 italic")
label.grid(column=0, row=0, columnspan=3, sticky='we')
l2 = tk.Label(root, text='Enter a number between 1 and 100',
fg='white', bg='blue')
l2.grid(row=1, column=0, sticky='we')
entry = tk.Entry(root, width=10, textvariable=Vars)
entry.grid(column=1, row=1, padx=5,sticky='w')
b1 = tk.Button(root, text="Guess", command=get_number)
b1.grid(column=1, row=1, sticky='e', padx=75)
l3 = tk.Label(root, width=40, fg='white', bg='red' )
l4 = tk.Label(root, width=40, fg='white', bg='black' )
root.mainloop()
while guess:
time.sleep(10)
Output for enter floating numbers:
Output after the guess was high:
Output after the guess was low:
Output You won and number of turns:

How to make label and entry start blank

Any idea on how to make all the entry and labels in my GUI start blank but then update when the calculate function happens? They currently start with a 0. I have tried many things but nothing has worked.
Here is code:
from tkinter import *
root = Tk(className="Page Calculator")
root.title("PgCalc")
read = IntVar()
total = IntVar()
left = IntVar()
percent = IntVar()
def calculate(event=None):
try:
left.set(total.get() - read.get())
percent.set(int(read.get()*100/total.get()))
except ZeroDivisionError:
print("ZeroDivisionError")
else:
print()
root.bind('<Return>', calculate)
read_label = Label(root,text="Pages Read:")
read_label.grid(column=1, row=1)
read_entry = Entry(root, width=8, textvariable=read)
read_entry.grid(column=2, row=1)
read_entry.focus()
total_label = Label(root,text="Total Pages:")
total_label.grid(column=1, row=2)
total_entry = Entry(root, width=8, textvariable=total)
total_entry.grid(column=2, row=2)
calculate_button = Button(root,text="Calculate",command= calculate)
calculate_button.grid(column=2, row=3)
percent_label = Label(root,text="Percent Finished:")
percent_label.grid(column=1, row=4)
left_label = Label(root,text="Pages Left:")
left_label.grid(column=1, row=5)
percentresult_label = Label(root,textvariable=percent)
percentresult_label.grid(column=2, row=4)
leftresult_label = Label(root,textvariable=left)
leftresult_label.grid(column=2, row=5)
root.mainloop()
IntVar() has a default value of 0. Even though they are IntVar, you can set strings as their value (note that when you try to get its value, you'll get an error if they still contain strings).
So you can simply do
read = IntVar()
read.set("")
But, since you are using Entry, you don't need any IntVar at all. You can directly get entry's value and cast it to an integer.
def calculate(event=None):
try:
leftresult_label.config(text=str(int(total_entry.get()) - int(read_entry.get())))
except ValueError:
print("Please enter a number")
#You need to remove textvariables from entries as well like below
total_entry = Entry(root, width=8)

Categories

Resources