How to allow user input on tkinter? - python

Hi I'm a beginner in Python and I'm working on a chatbot interface using tkinter. The problem i'm having is that I can't seem to type in the window after asking “Hi there, what’s your name?”. Instead, the conversation continues to the next line, stating Hi (the first entry stated) would you like to purchase a ticket?
Is there a way to allow user to enter their name, then after that, state the entry?
For example:
User: Hi
Bot: Hi there, what’s your name?
User: Tom
Bot: Hi Tom, would you like to purchase a ticket?
At the moment the program goes like this:
User: hi
Bot: Hi there, what’s your name?
Bot: Hi hi would you like to purchase a ticket?
I'm not sure what is causing the Bot to state another statement, before allowing user input, I've thought of storing name in name_var using StringVar(), then use that variable in the next line, but that didn't work.. any ideas how to fix this?
This is the code I have so far:
# Representing the parent window
root = Tk()
#Title of window
root.title('Chatbot')
#Function to send the message
def send():
send = "You =>" + entry.get()
text.insert(END, "\n" + send) if(entry.get()=="Hi" or entry.get()=="hi" or entry.get()=="Hi there" or entry.get()=="hi there" or entry.get()=="Hello" or entry.get()== "hello"):
text.insert(END, "\n" + "Bot => Hi there, whats your name ?")
name_var = str(StringVar())
text.insert(END, "\n" + "Bot => Hi " + name_var + " would you like to purchase a ticket?")
else:
text.insert(END, "\n" + "Bot => Sorry, I have no answer")
entry.delete(0, END) text = Text(root)
# Adding dimensions and send button of window
text.grid(row=0, column=0, columnspan=2)
entry = Entry(root, width=100)
send = Button(root, text="Send", command=send).grid(row=1, column=1) entry.grid(row=1, column=0)
root.mainloop()```

In your send callback, you just add the two messages right after another, without waiting for user input in between. Instead, you could split that in two: A generator function, yielding the next line in a complex conversation tree, and the send message just caring about putting that next message into the text field:
from tkinter import *
def message_generator():
if entry.get().lower() in ("hi", "hi there", "hello"):
# ask for name
yield "Hi there, whats your name ?"
# greeting with name
name_var = entry.get()
yield "Hi " + name_var + " would you like to purchase a ticket?"
# more conversation, possibly with diverging branches
# if entry.get() == "yes": ...
else:
yield "Sorry, I have no answer"
def send():
text.insert(END, "\nYou =>" + entry.get())
text.insert(END, "\nBot =>" + next(msg_gen))
entry.delete(0, END)
msg_gen = message_generator()
root = Tk()
root.title('Chatbot')
text = Text(root)
text.grid(row=0, column=0, columnspan=2)
entry = Entry(root, width=100)
entry.grid(row=1, column=0)
Button(root, text="Send", command=send).grid(row=1, column=1)
root.mainloop()
The main point here is that the message_generator will not be executed fully. When you first call the function, it just creates a generator instance; then when you call next in send, it executes the generator function up to the next yield statement and pauses there, picking up execution from that same point the next time next is called, thus providing a nice way to keep track of the current state of the conversation.

Related

Why does everythin sleeps for 0.5s when I use time.sleep(0.5) instead of only 1 function? [duplicate]

This question already has answers here:
How can I schedule updates (f/e, to update a clock) in tkinter?
(8 answers)
Closed 1 year ago.
I am making a responce bot in python with tkinter. For that when user inputs something I will give answer.
I have not yet completed it. I wanted that the bot should answer after some-time so it looks very nice.
My code:-
import tkinter
from tkinter import Message, messagebox
from tkinter import font
from tkinter.constants import CENTER, LEFT, RIGHT, BOTTOM, TOP
from tkinter import scrolledtext
from typing import Sized
import time
window = tkinter.Tk()
window.geometry("370x500")
window.configure(bg="orange")
#variables
running = True
verdana_12 = ('Verdana', '12')
verdana_10 = ('Verdana', '10')
verdana_9 = ('Verdana', '9')
msg=tkinter.StringVar()
#messages
greetings = ["hi", "hello", "hey", "what's up!"]
questions = [
' 1. What is python?',
' 2. Where to ask questions if I get stuck?',
' 3. How can I get example questions and quizes related to python?'
]
#items inb gui
info_text = tkinter.Label(window, text="Chat", bg="orange", font=verdana_12)
info_text.pack(padx=20, pady=5)
text_1 = tkinter.Label(window, text="Type your message: ", font=verdana_9, bg="orange")
text_1.place(x=0, y=476)
input_area = tkinter.Entry(window, justify=LEFT, textvariable=msg, font=verdana_12, width=16)
input_area.focus_force()
input_area.place(x=135, y=476)
chat_area = scrolledtext.ScrolledText(window)
chat_area.pack(padx=20, pady=5)
chat_area.config(state = "disabled")
#define message
message = f"You: {input_area.get()}\n"
#functions
#def afterwards():
# mine_msg = message.lower().strip().split()[1]
# if 1 == mine_msg:
# reply("Bot: Python is an interpreted high-level general-purpose programming language. Python's design philosophy emphasizes code readability with its notable use of significant indentation. More info at https://en.wikipedia.org/wiki/Python_(programming_language)")
#
# elif 2 == mine_msg:
# reply("Bot: If you get stuck in python programming, go to stackoverflow.com where you can ask questions related to any programming language!")
#
# elif 3 == mine_msg:
# reply("Bot: You can get quizes related to python in w3schools.org and example questions in w3resource.org.")
def reply(reply_msg):
str(reply_msg)
the_reply = f"{reply_msg}\n" #input_area = where person types.
chat_area.config(state='normal')
chat_area.insert(tkinter.INSERT, the_reply)
chat_area.yview('end')
chat_area.config(state='disabled')
input_area.delete(0, 'end')
def check_msg():
global message
print('Message =', message.strip())
try:
mine_msg = message.lower().strip().split()[1]
if greetings[0] == mine_msg or greetings[1] == mine_msg or greetings[2] == mine_msg or greetings[3] == mine_msg:
reply("Bot: Hello, here's how I can help you")
for i in questions:
reply(i)
#afterwards()
else:
reply("Bot: Couldn't understand your message, please type 'hi', 'hello', 'hey' or 'what's up!' to get responce.")
except IndexError:
pass
def write():
global message
if len(input_area.get().split()) > 0:
message = f"You: {input_area.get()}\n" #input_area = where person types.
chat_area.config(state='normal')
chat_area.insert(tkinter.INSERT, message)
chat_area.yview('end')
chat_area.config(state='disabled')
input_area.delete(0, 'end')
check_msg()
else:
reply('Please type something.')
def print_it():
message = f"You: {input_area.get()}"
print(message)
#button_send
send_button = tkinter.Button(window, text="Send", command=write, font=verdana_10, bg="gray", fg="white", width=26, height=1)
send_button.pack(padx=20, pady=5)
window.mainloop()
It freezes everything for 0.5 instead I want it to freeze only the write() function. Any help please.
Thank you!
One way to achieve this would be to use the .after method from tkinter to delay the message from being replied to for a period of time.
i = "My Message"
window.after(1000,lambda i=i: reply(i))
This would call the reply function with the message "My Message" after 1000ms or 1 second.

How to use entry widgets in Tkinter in a loop

I'm creating a Mad Libs game that has a lot of user inputs. I created a function to get all inputs in a list, given a list of the word types. There's no error message, but the window is blank. I tried printing the wordInputs list, and it comes up with:
[<bound method StringVar.get of <tkinter.StringVar object at 0x108763fd0>>,
and so forth. I presume this is because it deleted all of the widgets, but I figured it would do that only after I typed them in. I'm not exactly sure how to store the input into a variable either; do I create a button for that or something? Here's the code:
from tkinter import *
class Game:
#Gets user input for a specified type of word
def getWord(self, words):
wordInputs = []
for i in range(len(words)):
frame = Frame(self.windowSnowDay)
frame.pack()
Label(frame, text = "\nSnow Day\n________\n").grid(row = 1, column = 1, columnspan = 2)
Label(frame, text = "Enter a(n) " + words[i] + ":").grid(row = 2, column = 1)
word = StringVar()
Entry(frame, textvariable = word).grid(row = i + 2, column = 2)
wordInputs.append(word.get)
frame.destroy()
return wordInputs
#Executes "Snow Day" story from Mad Libs menu
def snowDay(self):
self.windowMadLibs.destroy()
self.windowSnowDay = Tk()
self.windowSnowDay.title("Snow Day")
self.windowSnowDay.geometry("200x200")
frame = Frame(self.windowSnowDay)
frame.pack()
#Collects words and stores them in a list
words = ["verb", "electronic device", "public place", "adjective", "verb ending with -ing", "color", "noun", "noun", "drink", \
"clothing item", "adjective", "3-dimensional shape", "adjective", "plural noun", "adjective", "feeling (adjective)", "food"]
wordInputs = self.getWord(words)
print(wordInputs)
self.windowSnowDay.mainloop()
#Prints "Snow Day" story with all inputted words
print("\nAll the children cheer in", emotion, "as they", verb, "that morning. They hear on the", device,"that the", place, end =' ')
print("is closed because of a snowstorm. They think of all the", adj, "things they could do today, such as", verb1, "on a", end = ' ')
print(color, noun + ". Maybe they can even make a snow" + noun1 + "! They go inside, where a warm cup of", drink, "awaits", end = ' ')
print("them. Before going outside, they put on their scarves and", clothing, "so that they don't get", adj1 + ". They", end = ' ')
print("make a snow" + noun1, "out of 3 large", shape + "s, but it quickly fell apart because the snow wasn't very", adj2, end = '. ')
print("After that, one of the", noun2, "attacked another, and it turned into a", adj3, "snowball fight. They were so", feeling, end = ' that ')
print("they went straight to bed. Oh well, I guess they can eat the leftovers of Mom's famous", food, "tomorrow!")
#Main function for Mad Libs
def madLibs(self):
self.windowMadLibs = Tk()
self.windowMadLibs.title("Mad Libs")
self.windowMadLibs.geometry("200x200")
frame = Frame(self.windowMadLibs)
frame.pack()
Label(frame, text = "\nMad Libs\n________\n").grid(row = 1, column = 1)
Button(frame, text = "Snow Day", command = self.snowDay).grid(row = 2, column = 1)
self.windowMadLibs.mainloop()
The main issue is it does not wait for user input, so it's basically going through the loop with all blank values in the entry, then nothing is filled.
(See my comments in the code for more detail)
The bound method StringVar.get result is from wordInputs.append(word.get) instead of wordInputs.append(word.get())
Also, multiple Tk() and .mainloop() calls tend to gum up the works.
This will get it going again, but window re-sizing will need to be addressed,
The prints reference variables that will need to be filled in, you may want a dictionary versus a list:
from tkinter import *
class Game:
#Gets user input for a specified type of word
def getWord(self, words):
wordInputs = []
for i in range(len(words)):
frame = Frame(self.windowSnowDay)
frame.pack()
Label(frame, text = "\nSnow Day\n________\n").grid(row = 1, column = 1, columnspan = 2)
Label(frame, text = "Enter a(n) " + words[i] + ":").grid(row = 2, column = 1)
word = StringVar()
entry = Entry(frame)
entry.grid(row = i + 2, column = 2)
button = Button(frame, text = "Done", command = lambda: word.set(entry.get()))
button.grid(row = i + 3, column = 2)
button.wait_variable(word) # waits for button to be activated and sets the variable.
wordInputs.append(word.get())
frame.destroy()
return wordInputs
#Executes "Snow Day" story from Mad Libs menu
def snowDay(self):
self.windowMadLibs.withdraw() # withdraw the main window.
self.windowSnowDay = Toplevel(self.windowMadLibs) # create a Toplevel instead of a new main.
self.windowSnowDay.title("Snow Day")
self.windowSnowDay.geometry("200x200")
frame = Frame(self.windowSnowDay)
frame.pack()
#Collects words and stores them in a list
words = ["verb", "electronic device", "public place", "adjective", "verb ending with -ing", "color", "noun", "noun", "drink", \
"clothing item", "adjective", "3-dimensional shape", "adjective", "plural noun", "adjective", "feeling (adjective)", "food"]
wordInputs = self.getWord(words)
print(wordInputs)
#Prints "Snow Day" story with all inputted words
print("\nAll the children cheer in", emotion, "as they", verb, "that morning. They hear on the", device,"that the", place, end =' ')
print("is closed because of a snowstorm. They think of all the", adj, "things they could do today, such as", verb1, "on a", end = ' ')
print(color, noun + ". Maybe they can even make a snow" + noun1 + "! They go inside, where a warm cup of", drink, "awaits", end = ' ')
print("them. Before going outside, they put on their scarves and", clothing, "so that they don't get", adj1 + ". They", end = ' ')
print("make a snow" + noun1, "out of 3 large", shape + "s, but it quickly fell apart because the snow wasn't very", adj2, end = '. ')
print("After that, one of the", noun2, "attacked another, and it turned into a", adj3, "snowball fight. They were so", feeling, end = ' that ')
print("they went straight to bed. Oh well, I guess they can eat the leftovers of Mom's famous", food, "tomorrow!")
#Main function for Mad Libs
def madLibs(self):
self.windowMadLibs = Tk()
self.windowMadLibs.title("Mad Libs")
self.windowMadLibs.geometry("200x200")
frame = Frame(self.windowMadLibs)
frame.pack()
Label(frame, text = "\nMad Libs\n________\n").grid(row = 1, column = 1)
Button(frame, text = "Snow Day", command = self.snowDay).grid(row = 2, column = 1)
self.windowMadLibs.mainloop()
g = Game()
g.madLibs()

How would add multiple logins to this python script

Hey could you help me with this. I would like to add multiple logins to this script, with each login running separate code. For example if you entered
Username: xyz
Password: zyz
It would run A Script saying
# file-input.py
f = open('helloworld.txt','r')
message = f.read()
print(message)
f.close()
And then if you entered
Username: abc
Password: cba
It would Run this script
'''Illustrate input and print.'''
applicant = input("Enter the applicant's name: ")
interviewer = input("Enter the interviewer's name: ")
time = input("Enter the appointment time: ")
print(interviewer, "will interview", applicant, "at", time)
# file-output.py
f = open('helloworld.txt','w')
f.write(interviewer)
f.write(" will Interview ")
f.write( applicant)
f.write(" at ")
f.write( time)
f.close()
Anyway here's the full code:
from tkinter import *
root = Tk()
username = "abc"
password = "cba"
username_entry = Entry(root)
username_entry.pack()
password_entry = Entry(root, show='*')
password_entry.pack()
def trylogin():
if username == username_entry.get() and password == password_entry.get():
'''Illustrate input and print.'''
applicant = input("Enter the applicant's name: ")
interviewer = input("Enter the interviewer's name: ")
time = input("Enter the appointment time: ")
print(interviewer, "will interview", applicant, "at", time)
# file-output.py
f = open('helloworld.txt','w')
f.write(interviewer)
f.write(" will Interview ")
f.write( applicant)
f.write(" at ")
f.write( time)
f.close()
else:
print("Wrong")
button = Button(root, text="Enter", command = trylogin)
button.pack()
root.mainloop()
Thanks For helping
when writing apps with GUI's, in my opinion it is much easier and more efficient to use an Object orientated approach.
For more details on how to implement tkinter applications using OOP please check out my guy Sentdex:
https://www.youtube.com/watch?v=HjNHATw6XgY&list=PLQVvvaa0QuDclKx-QpC9wntnURXVJqLyk
The code below takes userinputs and checks against the details we have set. If a ceratin username and password are enetered then it will run a certain function. At the moment i have only coded for one outcome, which is if the user enter "abc" ...
Code:
from tkinter import *
import tkinter as tk
class Application(Frame):
def __init__ (self, master):
Frame.__init__(self,master)
self.grid()
self.username1 = "abc"
self.password1 = "cba"
username_label = Label(self, text="Username:")
username_label.grid(row=0, column=0)
password_label = Label(self, text="Password:")
password_label.grid(row=1, column=0)
self.username_entry = Entry(self)
self.username_entry.grid(row=0, column=1)
self.password_entry = Entry(self, show='*')
self.password_entry.grid(row=1, column=1)
login = Button(self, text="Login", command= self.tryLogin)
def tryLogin(self):
usernameInput = self.username_entry.get()
passwordInput = self.password_entry.get()
if usernameInput == self.username1 and passwordInput == self.password1:
self.function1()
else:
print("wrong")
# you can add elif username and password equals something else here but i
#suggest using my alternative method explained below using a database.
def function1(self):
print("executing function 1")
window = tk.Tk()
app = Application(window)
window.mainloop()
HOWEVER In my opinion the best way to execute what you are trying to do is to create a databse for your accounts. In thats database you should by default add the accounts with the function that they will execute.
For instance:
create a database (use sqlite3) with fields username, password, function
Then when a user logins in, you can query the database. If the user enters login details which exist then return the functionName for that respective account and execute it.
*check this link out on how to create a login function and database:
https://www.youtube.com/playlist?list=PLBRzFm0BKuhaQyJ37KiI9rg3R3CC6-v9e
I would show you how to do this but you have to do some research now i have pinpointed you to 2 of the best guys and they cover how to do all these things.
Good luck m8!

Why does it return the none in python shell

I am trying to create a program which only allows you to write a certain amount of characters as a prompt while using a text box. When I try to run the program it returns none in the python shell and doesn't complete the function I would like it to. I would like it to write "your prompt has been posted" if there are under ten characters and write "The prompt is too long" if there are over 10 character. Thanks in advance for the help. It is greatly appreciated
label = Label(tk, text="Prompt:")
label.place(relx=0.1, rely=0.2, anchor=CENTER)
text = Text(tk, width=50, height=6, bg="gray")
text.place(relx=0.62, rely=0.2, anchor=CENTER)
def diary():
print("Why does this not work")
def begin():
while True:
answer = input(text.insert(INSERT, diary))
if len(answer) <= 10:
print("Your prompt has been posted")
else:
print("The prompt is too long")
button = Button(tk, text="Submit", command=begin)
button.place(relx=0.5, rely=0.5, anchor=CENTER)
The code never ends because you told it to run a loop forever without changing anything that would cause it to stop.
Also, whatever you think this code is doing, it's probably not doing it. I count at least three things wrong with this one line of code:
answer = input(text.insert(INSERT, diary))
The input command will read in put from the command line (technically, stdin), which is not something you typically do in a GUI. You are passing it the result of a call to text.insert, but text.insert isn't documented to return anything. Plus, you're giving text.insert a function where it expects a string.
If you want to insert what the function diary returns, you must a) define diary to do something, and b) call it as a function. For example:
def diary():
return "something"
...
text.insert(INSERT, diary())
If your real goal is to have begin get what the user has entered in the GUI and check the length, then you need to remove the while loop and replace the call to insert with get:
def begin():
answer = text.get("1.0", "end-1c")
if len(answer) <= 10:
print("Your prompt has been posted")
else:
print("The prompt is too long")

Python program getting stuck

I am new to Python and I am writing a program just for fun. My program consists of three .py files (let's say a.py, b.py, c.py). a will either call on the functions within either b or c, depending on the user's option. After it completes its first round it asks if the user would like to continue or simply exit the program. If they chose continue it asks again whether it should run b or c.
The problem I ran into is that the first time around, a will call the functions in either perfectly fine, it runs smoothly, and then when I select to continue it again calls either function perfectly fine, it will go into the function, but then the function gets stuck in its first step.
The program is not terminating, not giving an error. It accepts the raw_input variable but it will not continue. I was wondering if there was some way to force it to accept the variable and then continue the process (getting it 'unstuck'). I already tried putting pass on the next line. That didn't work.
Here are the steps it takes starting from the request to continue:
Continue = tkMessageBox.askyesno('Cypher Program', 'I have completed the task'
+ '\nWould you like to do anything else?')
## This is in a.py;
if Continue == True:
cyp()
def cyp():
global root
root = Tk()
root.title("Cypher Program")
root['padx'] = 40
root['pady'] = 20
textFrame = Frame(root)
Label(root, text = 'What would you like to do?').pack(side = TOP)
widget1 = Button(root, text = 'Encrypt a file', command = encrypt)
widget1.pack(side = LEFT)
widget2 = Button(root, text = 'Decrypt a file', command = decrypt)
widget2.pack(side = RIGHT)
widget3 = Button(root, text = 'Quit', command = quitr)
widget3.pack(side = BOTTOM)
root.mainloop()
def encrypt():
root.destroy()
encrypt3.crypt()
##Then from there it goes to b.py;
def crypt():
entry('Enter a file to encrypt:', selectFile)
def entry(msg1, cmd):
global top
top = Toplevel() ##changed it to Toplevel
top.title("File Encrypion")
top['padx'] = 40
top['pady'] = 20
textFrame = Frame(top)
entryLabel = Label(textFrame)
entryLabel['text'] = msg1
entryLabel.pack(side = LEFT)
global entryWidget
entryWidget = Entry(textFrame)
entryWidget['width'] = 50
entryWidget.pack(side = LEFT)
textFrame.pack()
button = Button(top, text = "Submit", command = cmd)
button.pack()
button.bind('<Return>', cmd)
top.mainloop()
def selectFile():
if entryWidget.get().strip() == "":
tkMessageBox.showerror("File Encryption", "Enter a file!!")
else:
global enc
enc = entryWidget.get().strip() + '.txt'
top.destroy() ##gets stuck here
##This is the rest of crypt(). It never returns to the try statement
try:
view = open(enc)
except:
import sys
sys.exit(badfile())
text = ''
You need to restructure your code to only create the root window once, and only call mainloop once. Tkinter is not designed to be able to create and destroy the root multiple times in a single process.
If you need multiple windows, create additional windows with the Toplevel command.

Categories