I am trying to run a simple script with a simple GUI that compares two strings. As you can see I added two entry fields and a check button that runs my check function. At the start the .focus() is on the first entry widget then I need to input a value select the second entry widget input a value and then press check for the script to compare the two given values.
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
win.title("Vitek magacin")
win.geometry("200x150")
win.configure(background='gold')
def check():
c1=no1.get()
c2=no2.get()
if c1 == c2:
print("You win a silly price")
else:
print("You win nothing old men")
no1=tk.StringVar()
no2=tk.StringVar()
inputa = ttk.Entry(win, width=12, textvariable=no1)
inputa.grid(column=0, row=1)
inputa.focus()
inputb = ttk.Entry(win, width=12, textvariable=no2)
inputb.grid(column=0, row=2)
ButtonCheck = ttk.Button(win, text='Check',
command=check)
ButtonCheck.grid(column=0, row=3)
win.mainloop()
So what I am trying to make it do is:
When I fill entry one for it to switch focus to entry two and when entry two is filled I want it to run the check function, delete the entry fields, and start over, because these inputs are going come from a bar code reader and I want it to go as automated as possible. Can anyone please help ?
I would solve this with the bind method.
This is a little wrong and have some disadvantages, but it works if you don't press any additional keys. If you press 'fn' 12 times, for example, there will be a problem, but I think you can solve it.
The idea is that the program counts keystrokes and compares them with the width of the entry field, if the entry field is filled completely, then the program focuses on the second entry field, if the second entry field is also filled completely, then the program performs the function.
from tkinter import *
root = Tk()
root.title("Vitek magacin")
root.geometry("200x150")
root.configure(background='gold')
s = 0
def check():
c1=inputa.get()
c2=inputb.get()
if c1 == c2:
print("You win a silly price")
else:
print("You win nothing old men")
def checker(event):
global s
s += 1
if s==inputa['width']:
inputb.focus()
if s==inputa['width']+inputb['width']:
check()
inputa = Entry(root, width=12)
inputa.grid(column=0, row=1)
inputa.focus()
inputb = Entry(root, width=12)
inputb.grid(column=0, row=2)
ButtonCheck = Button(root, text='Check', command=check)
ButtonCheck.grid(column=0, row=3)
root.bind('<Key>', checker)
root.mainloop()
Related
Very new to python and tkinter. I would like to create multiple comboboxes, independently selecting from a single dictionary, then print the corresponding values after clicking the "Submit" button. All I can manage from looking at somewhat similar questions without errors and frustration is printing the selected keys.
Simply put, I want to select A4 in the first combobox and A7 in the second combobox, click Submit, producing this result in the python shell:
8.3 x 11.7
2.9 x 4.1
Instead of this result:
A4
A7
Any advice would be appreciated.
import tkinter as tk
from tkinter import ttk
# Create instance
win = tk.Tk()
# Dictionary for combobox selection
Paper_sizes = {"":"", "A9":"1.5 x 2.0", "A8":"2.0 x 2.9",
"A7":"2.9 x 4.1", "A6":"4.1 x 5.8", "A5":"5.8 x 8.3",
"A4":"8.3 x 11.7", "A3":"11.7 x 16.5", "A2":"16.5 x 23.4",
"A1":"23.4 x 33.1", "A0":"33.1 x 46.8"}
# Combobox 1
ttk.Label(win, text="Choose a paper size").grid(column=1, row=0)
paper1 = tk.StringVar()
paper1_chosen = ttk.Combobox(win, width=12, textvariable=paper1, state='readonly')
paper1_chosen['values'] = sorted(list(Paper_sizes.keys()))
paper1_chosen.grid(column=1, row=1)
paper1_chosen.current(0)
# Combobox 2
ttk.Label(win, text="Choose a 2nd paper size").grid(column=1, row=2)
paper2 = tk.StringVar()
paper2_chosen = ttk.Combobox(win, width=12, textvariable=paper2, state='readonly')
paper2_chosen['values'] = sorted(list(Paper_sizes.keys()))
paper2_chosen.grid(column=1, row=3)
paper2_chosen.current(0)
# Button Click Event Function
def submit_button():
print(paper1_chosen.get())
print(paper2_chosen.get())
# Adding a Button
action = ttk.Button(win, text = "Submit", command = submit_button)
action.grid(column=2, row=4)
# Start GUI
win.mainloop()
Does changing your function to this, help you?
def submit_button():
key1 = paper1_chosen.get()
key2 = paper2_chosen.get()
print(Paper_sizes[key1])
print(Paper_sizes[key2])
Nothing much is happening inside, all thats happening is that you are taking the key from the combobox selection and printing out the corresponding value pair.
Hope you understood, let me know if you have more doubts.
Cheers
I am struggling to find the correct search terms to find an answer to my question. I have a program that is working, but I am trying to minimize the number of clicks required to make it work. I have a tkinter interface that takes a 7 digit input and looks it up in a spreadsheet and then displays some relevant information. Right now, the user scans a barcode which puts 7 digits in an entry box like this:
workorder = tk.Entry(root)
workorder.focus()
canvas1.create_window(175, 800, window=workorder)
Then, the user has to press the enter key to start the function that uses those 7 digits:
def f1(event):
getPartNumber()
root.bind('<Return>', f1)
I am trying to find a way to start the function automatically so a keyboard is not required. I was trying to do something like:
if len(str(workorder)) == 7:
getPartNumber()
However, it seems like this only works to check the length of an entry after the entry has been made. Is it possible to check on the state of the entry box before the entry is made?
You can associate a variable with the entry widget, and then set a trace on the variable. The trace will call a function whenever the data in the widget changes. Inside the trace you can examine the length of the data and call a function when the length is 7.
Here's an example that updates a label when you enter 7 characters:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar()
entry = tk.Entry(root, textvariable=var)
label = tk.Label(root, text="", anchor="w")
entry.pack(side="top", fill="x")
label.pack(side="top", fill="x")
def boom(*args):
if len(var.get()) == 7:
label.configure(text="Boom!")
var.trace("w", boom)
root.mainloop()
I am just in the middle of creating an entry form for a program, and it seems that I have got stuck with the logic on this one.
Basically I wanted to design a dropdwon-list, which adds words to an array and displays these words as little buttons beneath it. When you click the buttons they disappear again and remove themselfs from the array.
Simple enough, I thought. The adding worked fine so far. But removing not so much... There is a logic error with the button array and I can't seem to figure it out!
I extracted the code for reviewing,
any help is greatly appreciated!
Word adding Window
import tkinter as tk
from tkinter import ttk
def rel_add(*args):
rel_array.append(tkvar.get())
print(rel_array)
rel_buttons_update()
def del_button(i):
print(i)
del rel_array[i]
print(rel_array)
rel_buttons[i].grid_remove()
# del rel_buttons[i]
rel_buttons_update()
def rel_buttons_update():
for i, rel in enumerate(rel_array):
rel_buttons.append(tk.Button(rel_grid, text=rel, font="Helvetica 7", command=lambda c=i: del_button(c)))
rel_buttons[i].grid(column=i, row=0, sticky="nw")
rel_array = []
rel_buttons = []
win = tk.Tk()
tkvar = tk.StringVar(win) # Create a Tkinter variable
choices_words = ["oolbath", "pflanze", "haus", "wasser", "brimbambum"] # Create Variable List
tkvar.set('Related Words...') # set the default option
choices_words.sort() # Sort List
tk.Label(win, text="Related Words: ").grid(row=0,column=0, sticky="w")
rel = tk.OptionMenu(win, tkvar, *choices_words)
rel.grid(row=0,column=1, sticky="w")
# Callbuck Function for Dropdwon Menu
tkvar.trace("w", rel_add)
rel_grid = tk.Frame(win)
# Display the Buttons for the related Words
rel_grid.grid(row=1,column=1, sticky="w")
win.mainloop()
The main problem is that you keep recreating the same buttons over and over, so rel_buttons contains many more elements than you expect.
As a simple experiement, add a print statement to rel_buttons_update like this:
def rel_buttons_update():
for i, rel in enumerate(rel_array):
rel_buttons.append(ttk.Button(rel_grid, text=rel, command=lambda c=i: del_button(c)))
rel_buttons[i].grid(column=i, row=0, sticky="nw")
print('in update, rel_buttons is now', rel_buttons)
You'll notice that there is one button the first time you use the option menu, three buttons the second time, six buttons the third time, and so on.
You need to either only create new buttons, or delete all the old buttons before recreating them.
I'm trying to make a tkinter GUI with a certain amount of buttons and entry fields that is specified by the user, so in this code below for example if the user specifies the variable number to be 3 I want there to be 3 entry boxes and , and I want to set it up so that if I type a value into the first field and click the button next to it, I want that button to read the value from the entry field next to it. I also need to assign each entry field to a variable that will be created through the same iterative loop. However, I'm having difficulty especially in regards to mapping the buttons to the entry fields, as I always seem to run up against an error with the text "AttributeError: 'NoneType' object has no attribute 'get'". Would anyone be able to either fix my code or help me find an alternative solution to the problem? Sorry if the description's a bit confusing.
This is different from just a problem of just getting the contents of the entry widget as I need it to create a certain amount of entry widgets and buttons using iteration, and the question that my question has been marked a duplicate of doesn't explain how to iteratively map each entry field to each button. For example, if I enter 3 as the variable, I need the program to create entry field 1, entry field 2 and entry field 3 and button 1, button 2 and button 3 and then map each button to its respective entry field using iteration.
I've tried using dictionaries, but this doesn't seem to help much.
import tkinter as tk
root = tk.Tk()
number = 3
d={}
def callBack():
print(d["E{0}".format(i)].get())
return
for i in range(0,number):
d["E{0}".format(i)] = tk.Entry(root)
d["E{0}".format(i)].grid(row=i, column=0)
d["B{0}".format(i)] = tk.Button(root, text="test", command=callBack)
d["B{0}".format(i)].grid(row=i, column=1)
The solution to "'NoneType' object has no attribute 'get'" has been asked probably a hundred times on this site, and the answer is always the same.
In python, when you do x = y().z(), x will be given the value of z(). In the case of x=tk.Entry(...).grid(...), x will be None because grid(...) always returns None. The solution is to always call grid or pack or place separate from when you create a widget.
You also claim you are having problems with dictionaries, but I don't see any problem in your code other than you are making it more difficult than necessary. You can directly use i as an index without having to build up a string for the index. If you need to keep track of both buttons and entries, I recommend two variables rather than one.
Part of the problem may also have to do with the fact you're trying to do something very odd in your command. You're trying to call the get method of the entry, but that's pointless since it simply returns a value that gets thrown away. In almost all cases, the correct solution is to write a proper function rather than trying to squeeze functionality into a lambda.
Example:
def handle_click(i):
entry = entries[i]
print("the value is {}".format(entry.get()))
buttons = {}
entries = {}
for i in range(0,number):
entry = tk.Entry(root)
button = tk.Button(root, text="test", command=lambda i=i: handle_click(i))
buttons[i] = button
entries[i] = entry
entry.grid(row=i, column=0)
button.grid(row=i, column=1)
You need to save the Entry and Button before calling grid:
import tkinter as tk
number = 3
root = tk.Tk()
def get_on_click(widget_dict, entry_name):
def on_click():
result = widget_dict[entry_name].get()
print("%s = %s" % (entry_name, result))
return result
return on_click
d = dict()
for i in range(0, number):
entry_name = "E{0}".format(i)
button_name = "B{0}".format(i)
print(entry_name, button_name)
d[entry_name] = tk.Entry(root)
d[entry_name].grid(row=i, column=0)
d[button_name] = tk.Button(root, text="test", command=get_on_click(d, entry_name))
d[button_name].grid(row=i, column=1)
root.mainloop()
This should help you get started.
In your comment, you ask how to save the value in the Entry. I would create a class to handle everything:
import tkinter as tk
number = 3
root = tk.Tk()
class EntryButton(object):
def __init__(self, root, number):
self.number = number
self.entry = tk.Entry(root)
self.button = tk.Button(root, text="test", command=self.on_click)
self.entry.grid(row=number, column=0)
self.button.grid(row=number, column=1)
self.value = None
def on_click(self):
self.value = self.entry.get()
storage = dict()
for i in range(0, number):
storage[i] = EntryButton(root, i)
root.mainloop()
for i in range(0, number):
value = storage[i].value
print(f"storage[{i}] = {value}")
As you can see, this eliminates a lot of extra work.
For get text from entry
Entry.get("1.0", "end-1c")
# 1.0 for get first line.
# end-1c for if last letter space, this deletes it.
More info
I keep getting this issue in my code when I try to use the retrieve command that I had made, I want the button named 'retrieve' to get the information in the entry box. This will then trigger the strGame command.
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1699, in __call__
return self.func(*args)
TypeError: retrieve() missing 1 required positional argument: 'entry'
My code:
from tkinter import *
verif = False
true=True
items=['pistol','knife','sword']
command=False
root=Tk()
root.iconbitmap('GameLogo.ico')
strvar=StringVar()
root.title('RPG Game')
root.geometry('900x600')
root.resizable(False,False)
frame1 = Frame(root, relief=RAISED)
frame1.pack(side=LEFT, fill=Y, padx=10, pady=10)
entry = Entry(frame1).pack(fill=X)
def retrieve(entry):
result=entry.get()
return result
retreive = Button(frame1, command=retrieve, text="Click to input").pack(fill=X)
def srtGame():
try:
if retrieve(e1)==str('drop'):
print("HELLO")
command = True
print ("Your inventory:", items)
dropItem = input("Which item do you want to drop? ")
for i in range(len(items)):
if items[i] == dropItem:
items.remove(dropItem)
print("Your", dropItem,"has been dropped. ")
print("Your inventory", items)
verif = True
if verif == False and command == True:
print("You don't have that item. Please enter another command.")
except:
IndexError
StartGame = Button(frame1, text="Start game.", relief=RAISED, command=srtGame).pack(fill=X)
GameOutput = Label(frame1, textvariable=strvar, relief=RAISED).pack(fill=X)
root.mainloop()
There are numerous errors in this program, which I will clarify here.
Useless variable assignments
The lines that goes like:
widgetVar = Widget(args).geometry(args)
Such as:
entry = Entry(frame1).pack(fill=X)
retreive = Button(frame1, command=retrieve, text="Click to input").pack(fill=X)
Is surely not doing what you intended. By chaining the construction of the widget along with geometry call, what really happens is: Widget(...) returns an instance, on which pack() or other geometry manager is called, that in turn returns None.
Hence all these variables are None, and if you need to store a reference you should break it down to two different lines of code.
Unnecessary "middlemen"
As I understand, you want your game to start (probably load another window/screen) on clicking Start game.
You can just add code to check the Entry contents when the user presses Start game directly, instead of having a button to Input that really does nothing useful as such.
Understand Tkinter vars
The point of using Tkinter vars is to avoid explicitly accessing the Widget to check data that it contains. For example, if you bind StringVar to the Entry, you no longer need to access the Entry object directly.
Mixing command-line and GUI
I would recommend using the Dialog in Tkinter, that can be used to create a pop-up with a text field, to ask the user for which item to drop.
Working Code
from Tkinter import *
items = ['pistol', 'knife', 'sword']
root = Tk()
strvar = StringVar()
root.title('RPG Game')
root.geometry('900x600')
root.resizable(False, False)
frame1 = Frame(root, relief=RAISED)
frame1.pack(side=LEFT, fill=Y, padx=10, pady=10)
entry = Entry(frame1, textvariable=strvar)
entry.pack(fill=X)
def srtGame():
if strvar.get() == str('drop'):
print("HELLO")
print("Your inventory:", items)
# TODO:
# Use Dialog window to ask for item to drop
# Let that be stored in `dropItem`
dropItem = None # Temporary
# Better than using for loop for this
if dropItem in items:
items.remove(dropItem)
print("Your", dropItem, "has been dropped. ")
print("Your inventory", items)
else:
print("You don't have that item. Please enter another command.")
startButton = Button(
frame1, text="Start game.", relief=RAISED, command=srtGame)
startButton.pack(fill=X)
GameOutput = Label(frame1, textvariable=strvar, relief=RAISED)
GameOutput.pack(fill=X)
root.mainloop()