nested while loop in python tkinter - python

The following code searches a text file for a name and displays the related number in a tkinter entry box in Python.
so original text file includes:
bob 19
dan 20
shayne 17
I would like add another nested loop so that if there are two names the same then two numbers are returned to the entry box. Sorry, I am new to Python, have tried but always come up with an error.
bob 18
bob 19
dan 20
shayne 17
#https://www.youtube.com/watch?v=lR90cp1wQ1I
from tkinter import *
from tkinter import messagebox
race = []
def displayInfo(race, name):
found = False
pos = 0
while pos < len(race) and not found:
if race[pos][0] == name:
found = True
pos+=1
if found:
return race[pos-1][1]
else:
messagebox.showerror(message = "Invalid input, please try again.")
def clickArea():
fin.set(displayInfo(race, name.get()))
def createlist():
raceFile = open ("C:/python/files/number_list.txt", 'r')
for line in raceFile:
racer = line.split()
race.append(racer)
raceFile.close()
return race
root = Tk()
root.title("Read From text File/List GUI")
Label(root, text="Name").grid(row=0, column=0)
name = StringVar()
Entry(root, textvariable=name).grid(row=0, column =1)
Label(root, text="Finish Time").grid(row=2, column=0)
fin=IntVar()
Label(root, textvariable=fin).grid(row=2, column=1)
button = Button(root, text="Finish Time", command=clickArea)
button.grid(row=3, column=0, columnspan=2)
createlist()
print(race)

your question is not related to tkinter, so I made the code without it.
It works like this: you enter the name you're looking for, then it looks for matches using the count method. If there is a match, then the index is written to the 'B' array. Further, since there is a space between the name and number, we take the space index + 1 and start outputting the string from this position to the end.
name = input('who do you want to find: ') + " "
with open("number_list.txt", "r") as file:
A = file.readlines()
#remove program entry '\n'
for i in range(len(A)):
A[i] = A[i].strip()
#getting matching names
B = [] #the court records the names we need
for i in A:
if i.count(name): #truth check
#this notation is equivalent to the notationsi: if i.count(name) == 1:
B.append(i)
print('the following numbers match:')
for i in B:
index_space = i.index(' ') + 1
print(i[index_space:])

If you want to get all the values for a name, you need to go through all the items in race:
def displayInfo(race, name):
# go through the race list and return values for given name
found = [x[1] for x in race if x[0] == name]
if found:
# return the values separated by comma
return ", ".join(found)
# no item found for the given name
return "not found"

Related

Please suggest with typeerror in python

I got a question which says "Write a function user prints even number from list). We have to ask the user to input a list. I am getting a "Type Error: not all arguments converted during string formatting". Please help were am I wrong.
def even_no(x):
a = x.split()
new_list = []
for i in a:
if i % 2 == 0:
new_list.append(i)
input_no = input("Enter number sequence: ")
print(even_no(input_no))
% is also use for string formatting, and since the split method belongs to string, it also returns a string, hence, the interpreter tries to format it.
change the line:
if i % 2 == 0:
to
if int(i) % 2 == 0:
that your code should work.
As a side note, your function won't print anything because there's no return for the even_no function
You forgot to convert the numbers from str to int. map can be used for this. Also you did not return your list.
def even_no(x):
a = map(int, x.split())
new_list = []
for i in a:
if i % 2 == 0:
new_list.append(i)
return new_list
input_no = input("Enter number sequence: ")
print(even_no(input_no))
Execution example:
Enter number sequence: 10 20 30
[10, 20, 30]
A couple of issues to be noted.
The first one has already been mentioned, which is the need to convert strings to integers.
The second is with the following line:
input_no = input("Enter number sequence: ") .
When I tested, the function "even_no" would not execute with the above line present as a global variable.
To overcome this problem I used tkinter and a class in the following code.
Please note: This allows the user to enter a number or numbers into an entry field. Each number should be separated by a space. Only numbers should be used.
If any even numbers are entered, they will be printed into a list in the python shell. If No even numbers are entered, an empty list will be printed. If non-numeric characters are entered, the Value Error is handled to guide the user on the correct method of input.
import tkinter as tk
from tkinter import Tk, messagebox
import tkinter.filedialog
from tkinter import Tk, Label, Button
from tkinter import *
class Control(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.controller = self
self.shared_data = {
"input_no": tk.StringVar(),
}
self.title('Even')
self.entry = Entry(self, textvariable=self.shared_data["input_no"])
self.entry.pack()
self.enterbutton = tk.Button(self, text='Enter',
command=(self.even_no)
)
self.enterbutton.pack()
def even_no(self):
try:
user_input = self.shared_data["input_no"].get()
a = user_input.split()
new_list = []
for i in a:
if int(i) % 2 == 0:
new_list.append(int(i))
print(new_list)
except ValueError:
print('Invalid Entry. Please enter numbers only. \n'
'Please make sure that each number is separated by a space.')
Control1 = Control()
Control1.mainloop()
Your indentation was a bit so I fixed that and you also forgot to use return. I fixed all of that including your TypeError. Here is the code:
def even_no(x):
a = x.split()
new_list = []
for i in a:
if int(i) % 2 == 0:
new_list.append(i)
return new_list
input_no = input("Enter number sequence: ")
print(even_no(input_no))

How to use bind "<Return>" to call a function

I know my question sounds very much like many previous questions but I can honestly not figure it out in the context of my program. I have an algorithm for the Collatz Conjecture that I would like to run through a Tkinter GUI (everything works just fine through the terminal).I have tried to bind the relevant function to the Return key and to a button but I get the same error message for both methods of entering data, which I will show below. I get the output to work perfectly on the GUI if I input through the terminal.
What I have tried is best explained through the code below. (The code above the #### line has mostly to do with making the GUI appear over my Spyder IDE and not hiding behind it.)
Code:
from tkinter import *
root = Tk()
import os
import subprocess
import platform
def raise_app(root: Tk):
root.attributes("-topmost", True)
if platform.system() == 'Darwin':
tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {} to true'
script = tmpl.format(os.getpid())
output = subprocess.check_call(['/usr/bin/osascript', '-e', script])
root.after(0, lambda: root.attributes("-topmost", False))
########################################################################
lst = []
def collatz(num):
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
def main(event):
collatz(num)
#Input Box
input = Entry(root, width = 10, bg = "light grey")
input.grid(row = 0, column = 0, sticky = W)
input.get()
input.bind("<Return>", main)
##Button
#button1 = Button(root, width = 10, text = "Run", command = main)
#button1.grid(row = 1, column = 0, sticky = W)
##Output box
output1 = Text(root, width = 100, height = 10, bg = "light grey")
output1.grid(row = 3, column = 0, sticky = W)
output2 = Text(root, width = 50, height = 1, bg = "white")
output2.grid(row = 2, column = 0, sticky = W)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
########################################################################
raise_app(root)
root.mainloop()
When I run the code as is, the input box appears but when I click return, I get an error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "/anaconda3/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "/Users/andrehuman/Desktop/Python/programs/Collatz Conjecture/Collatz_alt3.py", line 43, in main
collatz(num)
NameError: name 'num' is not defined
Exactly the same if I try and link a button to the "main" function.
When I comment the input and buttons out, and enter the number through the terminal, everything works as expected. The list of iteration numbers appear in the text box as it should. (And I can even get a Matplotlib graph to display the data visually in the terminal.) If I can get this problem sorted out I want to try and display (or embed) the Matplotlib graph in the GUI.
Anyway, that's it. Any help will be greatly appreciated.
Andre Human
name 'num' is not defined occurs because you're calling collatz(num), but the program does not understand what value you are referring to when you say num. You should assign a value to that name before using it. I assume you want the value to be the contents of your input box.
def main(event):
num = int(input.get())
collatz(num)
You will also need to copy your output1.insert and output2.insert lines to the inside of main. Right now, those lines execute before the window even appears to the user, so there's no way that they can enter a number fast enough to get collatz to trigger before the text gets written. And changing lst after the fact does nothing to the text, since it's not smart enough to notice that the list has changed.
def main(event):
num = int(input.get())
collatz(num)
#delete previous contents of text boxes
output1.delete(1.0, END)
output2.delete(1.0, END)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
Another problem is that successive calls to collatz will cause lst to grow and grow, because the contents of the list from previous calls is still present. Try entering 4 into the text box, and press Enter a few times. The output will go from 2 to 4 to 6... That's not right.
This is something of a natural hazard when using mutable global state. One possible solution is to reset lst at the beginning of each collatz call.
def collatz(num):
lst.clear()
#rest of function goes here
... But I'm more inclined to make lst local to the function, and return it at the end.
def collatz(num):
lst = []
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
return lst
def main(event):
num = int(input.get())
lst = collatz(num)
#delete previous contents of text boxes
output1.delete(1.0, END)
output2.delete(1.0, END)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
#later, just before mainloop is called...
#lst doesn't exist in this scope, so just set the text to a literal value
output1.insert(END, "[]")
output2.insert(END, "Number of iterations are: 0")

How to print Python Output in Entry widget?

I've searched through the whole Internet for how to do this, and nothing came to me. There were some similar topics, when programmers asked of how to parse 'int' numbers to the Entry output. But it is much simpler because you just use getters, then insert() - and voila.
I am trying to do the following thing. I print the text written in one line. And for each word I want to count how many times it appeared in the same text.
E.g., I print in my first Entry "one two one two three" - I get "0 0 1 1 0" in the second Entry widget.
Any non-space sequence of characters is considered a word.
from tkinter import *
class DM_3_1:
def __init__(self):
root = Tk()
root.geometry('250x150')
root.title("DiscreteMaths_3_1")
usertext = StringVar()
Label_1 = Label(root, text="Input")
Label_2 = Label(root, text="Output")
inputField = Entry(root, textvariable = usertext)
outputField = Entry(root)
inputField.bind('<Return>', lambda _: printLine())
def printLine():
counter = {}
for word in inputField.get():
counter[word] = counter.get(word, 0) + 1
Ans = print(counter[word] - 1, end=' ')
outputField.insert(0, str(Ans))
Label_1.grid(row = 0)
Label_2.grid(row = 1)
inputField.grid(row = 0, column = 1)
outputField.grid(row = 1, column = 1)
root.mainloop()
DM_3_1()
What I get in the output now: Here is the screenshot
As far as you can see, the application works, but there's 'NoneNoneNone...'(depends on the number of characters, including whitespaces) instead of '0 0 1 1 0'. How do I solve my problem? Where's a logical mistake? I guess, it's about the function, but I don't actually see the mistake.
You have set Ans to be equal to print rather than the value it was supposed to be. Also your for loop was getting every character rather than every word.
Corrected code:
from tkinter import *
class DM_3_1:
def __init__(self):
root = Tk()
root.geometry('250x150')
root.title("DiscreteMaths_3_1")
usertext = StringVar()
Label_1 = Label(root, text="Input")
Label_2 = Label(root, text="Output")
inputField = Entry(root, textvariable = usertext)
outputField = Entry(root)
inputField.bind('<Return>', lambda _: printLine())
def printLine():
counter = {}
words=inputField.get().split()
for word in words:
counter[word] = counter.get(word, 0) + 1
Ans = counter[word] - 1
print(Ans, end=" ")
outputField.insert(END, str(Ans))
Label_1.grid(row = 0)
Label_2.grid(row = 1)
inputField.grid(row = 0, column = 1)
outputField.grid(row = 1, column = 1)
root.mainloop()
DM_3_1()
edit:
As Mike-SMT pointer out its easier to use .split
code edited to use .split
So my solutions to this kind of counter is to track each word and keep a list of all the words. Then keep a list of all the unique words. The count each time a unique word appears in the complete list.
I restructured your code a bit to conform a bit better with standards.
I rewrote your printLine method to keep track of all the words in a string and create a dictionary that contains a list of all the unique words and how many times they show up in the string.
When writing a class you will need to learn to use self. to convert standard variables into class attributes. Class attributes can be accessed from anywhere in the class including methods within the class. Using regular variables will likely cause problems as they are not available to methods after __init__ has completed.
take a look at the below code.
import tkinter as tk
class DM_3_1:
def __init__(self, parent):
self.root = parent
self.root.geometry('250x150')
self.root.title("DiscreteMaths_3_1")
Label_1 = tk.Label(self.root, text="Input")
Label_2 = tk.Label(self.root, text="Output")
Label_1.grid(row=0)
Label_2.grid(row=1)
self.inputField = tk.Entry(self.root)
self.outputField = tk.Entry(self.root)
self.inputField.grid(row=0, column=1)
self.outputField.grid(row=1, column=1)
self.inputField.bind('<Return>', self.printLine)
def printLine(self, Event):
word_list = []
counter = 0
unique_words_in_string = []
total_times_word_appears = {}
for word in self.inputField.get().split():
word_list.append(word)
if word not in unique_words_in_string:
unique_words_in_string.append(word)
for word in unique_words_in_string:
counter = 0
for other_word in word_list:
if word == other_word:
counter += 1
total_times_word_appears[word]=counter
self.outputField.delete(0, "end")
self.outputField.insert("end", total_times_word_appears)
if __name__ == "__main__":
root = tk.Tk()
DM_3_1(root)
root.mainloop()

Create a variable from 2 Tkinter entries

I am trying to get the first three characters of a Tkinter entry, and then add them to another tkinter entry.
For example:
name = Entry=(root, text="Name: ")
age = Entry=(root, text="Age: ")
username = First three characters of name + age
Then I want the first three letters of their name to add to the age to create a user name.
If the user enters 'Taylor' as a 'name' and '13' as a 'age', I want to make a variable called 'username' which would contain 'Tay13'
Not sure if I made it too clear, but hopefully you understand. Thanks
EDIT: Just tried something else and it says 'StringVar' object is not subscriptable.
Just found out how to do it on Reddit. If any one else need this, then here is the answer:
username = name.get()[:3] + age.get()
This gets the first 3 letters from 'name' and adds 'age' on to the end of it.
Thanks to the people who helped.
You can do this to get the username. The [:3] pulls the first 3 letters of a string.
username = str(name[:3]) + str(age)
print username
Below example adds/updates id to users dictionary each time the button is pressed:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def update_users():
global users, name, age
user_id = name.get()[:3] + age.get()
users[user_id] = None
print(users)
if __name__ == '__main__':
root = tk.Tk()
users = dict()
name = tk.Entry(root)
age = tk.Entry(root)
update_btn = tk.Button(root, text="Update Users", command=update_users)
name.pack()
age.pack()
update_btn.pack()
root.mainloop()
This how to go about this create two entry widget to receive the content in the entry.Then your funtion to print the content in the entry and slice the first three letters of the name and print plus the age.
from tkinter import *
def Print_variable():
e1 = name.get()
e2 = age.get()
print(e1[:3] + e2)
root = Tk()
root.geometry("400x400")
name = StringVar()
e1 = Entry(root, textvariable=name) # this entry accept the name
e1.pack()
age = StringVar()
e2 = Entry(root, textvariable=age) # this entry accept the age
e2.pack()
b = Button(root, text="print name and age variable", command=Print_variable)
b.pack()
root.mainloop()

TkInter Entry Box Being Disabled

I'm working on a GUI Python program using Tkinter.
I have a function that is called when a button is pressed (and when the program is loaded). The program is currently unfinished and only checks data validation at this current point. As the default entry is current invalid, it throws an error.
However, after this point, the entry box is disabled and will not let me enter any data. I cannot figure out why this is happening and I was wondering if someone could tell me the reason so I can work on a solution.
Thanks
import sys
import random
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
root = Tk()
root.title("COSC110 - Guessing Game")
hint = StringVar()
guesses = []
guess_input = ''
def loadWordList(filename): #Load the words from a file into a list given a filename.
file = open(filename, 'r')
line = file.read().lower()
wordlist = line.split()
return wordlist
word = random.choice(loadWordList('words.txt'))
def getHint(word, guesses): #Get hint function, calculates and returns the current hint.
hint = ' '
for letter in word:
if letter not in guesses:
hint += '_ '
else:
hint += letter
return hint
def guessButton(guess, word, guesses):
guess = str(guess_input)
guess = guess.lower()
if not guess.isalpha():
is_valid = False
elif len(guess) !=1:
is_valid = False
else:
is_valid = True
while is_valid == False:
messagebox.showinfo("Error:","Invalid input. Please enter a letter from a-z.")
break
hint.set(getHint(word, guesses))
return hint
label_instruct = Label(root, text="Please enter your guess: ")
label_instruct.grid(row=1,column=1,padx=5,pady=10)
guess_input = Entry(root,textvariable=guess_input)
guess_input.grid(row=1, column=2)
guess_button = Button(root, text="Guess", width=15, command=guessButton(guess_input,word,guesses))
guess_button.grid(row=1, column=3,padx=15)
current_hint = Label(root, textvariable=hint)
current_hint.grid(column=2,row=2)
label_hint = Label(root, text="Current hint:")
label_hint.grid(column=1,row=2)
label_remaining = Label(root, text="Remaining guesses: ")
label_remaining.grid(column=1,row=3)
root.mainloop() # the window is now displayed
Any tips are appreciated.
There are two apparent problems.
Firstly, you shouldn't use
guess_button = Button(root, text="Guess", width=15, command=guessButton(guess_input,word,guesses))
because you can't call a function with arguments on the command config.
My suggestion would be to take a look here and use one of the proposed methods, I particularly like the one using functools and partial:
from functools import partial
#(...)
button = Tk.Button(master=frame, text='press', command=partial(action, arg))
with action being the function you want to call and arg the parameters you want to call separated by a comma.
Secondly, you are using
guess = str(guess_input)
which doesn't return the Entry typed text, use instead
guess = guess_input.get()
PS: Albeit not directly related to your question, you should use
if var is False:
instead of
if var == False:

Categories

Resources