tkinter unable to print a label before using root.destroy() - python

I want to print a message on the UI when the user wishes to exit.
This is a part of code relevant to it
if word in quit_words:
user_to_quit = True
Print = "Pleasure serving you!"
print_bot_reply(Print)
time.sleep(5)
root.destroy()
The print bot_reply function is as follows:
def print_bot_reply(response):
response = "\nKalpana: " + str(response)
label = Label(frame,text = response,bg="#b6efb1", borderwidth=5, relief="raised")
label.pack(anchor = "w")
The window is closing after 5 seconds, as desired but not displaying the message.
Please point me the mistake or suggest some other method to do this
Thanks!

Related

How do I test functions using pytest

Hello,
I need your help writing test functions for the following functions using pytest: add_new_user, register_user, login_verify
I am trying to test my login system program using the pytest module which is a requirement for the end-semester project. The following are functions I am having trouble testing:
Function without parameters, but rather get information from the user.
Function that appends information to a text file.
Function that reads from a text file.
Thank you in advance to the good samaritan.
1a) Python File.
def add_new_user():
"""The function enables the users to enter their information and register"""
# Gobal variables
global username
global password
global fullname
global username_entry
global password_entry
global fullname_entry
global new_user_window
# Creates another window on top of the main menu window
# for the user to enter their information.
new_user_window = Toplevel(main_window)
new_user_window.title("New User")
new_user_window.geometry("300x300")
# Create a string variable that corresponds to the user's
# username, password and full name
username = StringVar()
password = StringVar()
fullname = StringVar()
# Create "fill information" label.
label2 = Label(new_user_window, text = "Please fill in the info below", bg="gray", fg="pink")
label2.pack(fill=X, pady=20)
# Create username's label and the entry box for a user input
user_info_panel = Frame(new_user_window)
user_info_panel.pack(pady=20)
username_label=Label(user_info_panel, text="Username: ")
username_label.grid(row=0, column=0)
username_entry = Entry(user_info_panel, textvariable=username)
username_entry.grid(row=0, column=1)
# Create space between the username and password
Label(user_info_panel, text="").grid(row=1)
# Create password's label and the entry box for a user input
password_label = Label(user_info_panel, text="Password: ")
password_label.grid(row=2, column=0)
password_entry = Entry(user_info_panel, textvariable=password)
password_entry.grid(row=2, column=1)
# Create space between password and full name
Label(user_info_panel, text="").grid(row=3)
# Create full name's label and an entry box for the user input
fullname_label = Label(user_info_panel, text="Full name: ")
fullname_label.grid(row=4, column=0)
fullname_entry = Entry(user_info_panel, textvariable=fullname)
fullname_entry.grid(row=4, column=1)
# Create registration button for the user to their information.
register_btn = Button(new_user_window, text="Register", command=register_user)
register_btn.pack()
b) Test file
def test_add_new_user():
"""Verify that the add_new_user function works correctly.
Parameters: none
Return: nothing
"""
assert add_new_user("username") == "Fred69_Coder"
assert add_new_user("password") == "#123kitoVu"
assert add_new_user("fullname") == "Fred Okorio"
2a) Python file
def register_user():
"""The function writes the user's confidential information in a text file"""
# Initialize the False boolean variable
registered = False
# Get the information typed by the user in the entry boxes.
username_text = username.get()
password_text = password.get()
name_text = fullname.get()
try:
# Create the credentials text file and append the new user information
# in the already existing file.
with open("credentials.txt", "a") as credentials:
# Open the credential text file for reading and store a reference
# to the opened file in a variable named text_file.
with open("credentials.txt", "r") as text_file:
# Read the contents of the text
# file one line at a time.
for line in text_file:
# Split the data in line variable and store the split
# data in the login_info variable.
login_info = line.split()
# From all the information in the data base extract
# username information.
if username_text == login_info[1]:
registered = True
# If the user is already registered
if registered:
# Clear the user entry boxes
username_entry.delete(0, END)
password_entry.delete(0, END)
fullname_entry.delete(0, END)
# For debugging purpose
print("user already exist")
# Window for a failed login process.
failed_registration_window = Toplevel(new_user_window)
failed_registration_window.geometry("200x200")
failed_registration_window.title("warning!")
Label(failed_registration_window, text="user already exists.",
bg="gray", fg="pink").pack(fill=X, pady=20)
# Ok button exits the window
ok_btn = Button(failed_registration_window, text="Please login",
width="20",command=lambda:failed_registration_window.destroy())
ok_btn.pack(pady=20)
else:
credentials.write("Username: "+ username_text + " " + "Password: " + password_text + " " +"Name: "+ name_text + "\n")
# Clear the user entry boxes.
username_entry.delete(0, END)
password_entry.delete(0, END)
fullname_entry.delete(0, END)
# Window for a successful login process.
successful_registration_window = Toplevel(new_user_window)
successful_registration_window.geometry("200x200")
successful_registration_window.title("Success!")
Label(successful_registration_window, text="Sucessfully
Registered!", bg="gray", fg="pink").pack(fill=X, pady=20)
ok_btn = Button(successful_registration_window, text="OK", width="20", command = lambda:successful_registration_window.destroy())
ok_btn.pack(pady=20)
except FileNotFoundError as not_found_err: print(not_found_err)
except PermissionError as perm_err: print(perm_err)
b) Test file
def test_register_user():
"""Verify that the register_user function works correctly.
Parameters: none
Return: nothing
"""
3a) Python file
def login_verify():
try:
user = username_verify.get()
password = password_verify.get()
login = False
# Open the credential text file for reading and store a reference
# to the opened file in a variable named text_file.
with open("credentials.txt", "r") as text_file:
# Read the contents of the text
# file one line at a time.
for line in text_file:
login_info = line.split()
if user == login_info[1] and password == login_info[3]:
login = True
if login:
label1 = Label(login_window, text = "You've logged in successfully", fg="green")
label1.pack()
else:
failed_login_window = Toplevel(login_window)
failed_login_window.geometry("200x200")
failed_login_window.title("Warning!")
Label(failed_login_window, text="Invalid username. Please register", bg="gray", fg="pink").pack(fill=X, pady=20)
ok_btn = Button(failed_login_window, text="OK", width="20", command= lambda:failed_login_window.destroy())
ok_btn.pack(pady=20)
except IndexError as index_err:
print(index_err)
except FileNotFoundError as not_found_err:
print(not_found_err)
except PermissionError as perm_err:
print(perm_err)
b) Test file
def test_login_verify():
"""Verify that the login_verify function works correctly.
Parameters: none
Return: nothing
"""

How can I make tkinter wait for a line of code to finish before continuing?

so I am making a program on tkinter that gets a response from a server and depending on the answer, it will change the background color, to either green for success or red for error, the problem is that I realized that when running the code, the windows.after() method doesn't wait till is done to continue and when I do the request for the server, it have to do it three times to check if the response is correct, and it is suppossed to change the window background color each time, but it is only doing it one time. And not only the background color changing fails, also I want to change a label's text when it is doing the request,but it does it really quick and I'm not able to diferentiate the changes, so the question is: how can I
How can I make the program wait until one line finishes running to go to the next one and not everything happens at the same time and so fast?
Here is a piece of my code, I removed the request part because I'm trying to solve this problem first:
# import gpiozero
# import picamera
import json
import requests
import tkinter as tk
with open("config.json") as file:
config = json.load(file)
ENDPOINT = config["ENDPOINT"]
USUARIO = config["USUARIO"]
ESTACION = config["ESTACION"]
TIEMPO_ESPERA = config["TIEMPO_ESPERA"]
PIN_RELE = config["PIN_RELE"]
PATH_SALIDA = ENDPOINT + "Salida.getTicket/" + ESTACION + "/" + USUARIO + "/"
barcode = ""
# RELAY = gpiozero.OutputDevice(PIN_RELE, active_high=True, initial_value=False)
# CAMERA = picamera.PiCamera()
def check_scan_barcode(event=None):
info_label.config(text = "Wait...")
barcode = barcode_entry.get()
barcode_entry.delete(0, "end")
for i in range(3):
response = get_request(ENDPOINT + barcode)
if response["data"] == "True":
success()
open_barrier()
else:
error()
info_label.config(text = "Scan barcode")
def get_request(url):
response = requests.get(url)
response.raise_for_status()
response = response.json()
return response
def normal():
window.configure(bg="white")
info_label.configure(bg="white")
def success():
window.configure(bg="green")
info_label.configure(bg="green")
window.after(1000, normal)
def error():
window.configure(bg="red")
info_label.configure(bg="red")
window.after(1000, normal)
def open_barrier(barcode):
# CAMERA.capture(f"/home/pi/Pictures{barcode}.jpg")
# RELAY.on()
# window.after(TIEMPO_ESPERA, RELAY.off)
pass
window = tk.Tk()
# window.attributes('-fullscreen', True)
info_label = tk.Label(window, text= "Scan barcode.", font=("Arial", 40))
info_label.pack()
barcode_entry = tk.Entry(window, width=50)
barcode_entry.bind('<Return>', check_scan_barcode)
barcode_entry.pack(expand=True)
barcode_entry.focus()
window.mainloop()

Python threaded tkinter GUI unresponsive unless sleep time is used.

I'm using python 2.7 and have built a UI using Tkinter. I'm using threads and queues to keep the UI responsive while the main script is working. The basic summary is the script reads a text file, parses out some information on it and puts that info in a dictionary and a list in the dictionary, then uses that info to send TCP modbus request (using pyModbus). It then writes the responses/results to a text file. The results also get printed a Text widget included in the UI. The updates to the Text widget is handled by the mainloop.
I'm still fairly new to threads and queues and I'm having trouble figuring out this issue.
The problem I'm running into is I need to include a ~10ms sleep after it loops through each item in the list for the UI to remain responsive. If I include the sleep time it works as expected, if not it freezes up until the threaded process is finished then updates the UI all at once (as it would if threads weren't used). The 10ms sleep can be slightly shorter. Any amount longer also works.
Here's the code that handles updating the log:
textQueue = Queue.Queue()
def TextDisplay(message, disTime="false", myColor="black", bgColor="white"):
textQueue.put([message, disTime, myColor, bgColor])
class LogUI:
def __init__(self, master):
self.master = master
'''other ui elements, not relevent'''
self.mainLogFrame = Frame(self.master)
self.mainLogFrame.pack(side="top", fill="both", expand="yes", padx=5, pady=2)
self.logText = Text(self.mainLogFrame, height=2)
self.logText.pack(side="left", fill="both", expand="yes", padx=5, pady=2)
self.ThreadSafeTextDisplay()
def ThreadSafeTextDisplay(self):
while not textQueue.empty():
tempText = textQueue.get(0)
message = tempText[0]
disTime = tempText[1]
myColor = tempText[2]
bgColor = tempText[3]
message = str(message) + "\n"
'''bunch of formating stuff'''
logUI.logText.insert(END, message)
print message
#NOTE: tried to include a sleep time here, no effect
self.logText.after(10, self.ThreadSafeTextDisplay)
Here's the non-threaded function that's called when the user clicks a button.
def ParseInputFile():
'''non-threaded function, called when user clicks button'''
inputList = []
inputFile = mainUI.fullInFileEntry.get()
with open(inputFile, 'r') as myInput:
'''open file and put contents in list'''
for line in myInput:
inputList.append(line.strip())
outFile = mainUI.outFileEntry.get().strip() + '.txt'
i = 1
tableBol = False
inputDict = {}
inputKeys = []
tableID = None
for item in inputList:
'''parses out inputKeys, inputDict using regular expressions'''
runInputGetQueue.put([inputKeys, inputDict, outFile, inputFile])
Here's the threaded function that receives the parsed information and handles the modbus request (note: i tried commenting out the actual modbus request, no effect):
def RunInputThread():
time.sleep(.1)
while 1:
while not runInputGetQueue.empty():
tempGet = runInputGetQueue.get(0)
inputKeys = tempGet[0]
inputDict = tempGet[1]
outFile = tempGet[2]
inputFile = tempGet[3]
outFile = open(outFile, 'w')
TextDisplay('< Start of %s input file > ' % inputFile, True, 'blue')
for key in inputKeys:
'''loops through the keys in the dictionary'''
TextDisplay(key) #just used as an example.
for lineIndex in range(len(inputDict[key]['tableLines'])):
'''lots of code that loops thorugh the lines of input file, frequently calls the TextDisplay() function'''
TextDisplay(inputDict[key][lineIndex]) #just used as an example.
time.sleep(0.01) #UI will become unresponseive if not included.
outFile.close()
time.sleep(0.001)
Found a way to get the UI mostly responsive. As stated in the comments above, the queue was receiving stuff to fast the function would be constantly working causing the UI to lock up. I made it so it will print at most 5 messages before taking a 1ms break and recalling the function which allows the UI to 'catch up.' The messages are printed almost as fast as they come in.
The UI will be slightly non-responsive if you move it or resize it. I have no issues interacting with other UI elements while this is running though.
You could also change the while loop to a if statement if you don't mind it being slow. The one process i ran went from 14 seconds with an if statement down to around 5 or 6 seconds using the code below. It would be same as if you changed the pullCount break point to 1 instead of 5.
def ThreadSafeTextDisplay(self):
pullCount = 0
while not textQueue.empty():
pullCount += 1
tempText = textQueue.get(0)
message = tempText[0]
disTime = tempText[1]
myColor = tempText[2]
bgColor = tempText[3]
message = str(message) + "\n"
'''bunch of formatting stuff'''
logUI.logText.insert(END, message)
print message
if pullCount >= 5: break #you can change the 5 value to whatever you want, the higher the number the faster stuff will print but the UI will start to become unresponsive.
self.logText.after(1, self.ThreadSafeTextDisplay)

How to create a threaded progress bar in Python Tkinter

I'm creating a Tkinter GUI application using Python and one of the function in this application is pinging different IP addresses. I'm trying to show an undeterminate progress bar during the pinging process but I can't seem to make it work. Here is what I have tried:
def pingSwitches(ipDictionary):
validIPDictionary = {}
switchCounter = 1
popup = tk.Tk()
popup.wm_title("Processing...")
progress = ttk.Progressbar(popup, orient="horizontal", length=len(ipDictionary.keys()), mode="determinate")
progress.pack()
progress.start(50)
popup.mainloop()
for name, ip in ipDictionary.items():
#progress.step(1)
response = subprocess.Popen(["ping", "-n", "1", ip],stdout = subprocess.PIPE).communicate()[0]
if((b"unreachable" in response) or (b"timed out" in response) or (b"demande" in response)):
print(ip + " is down")
else:
validIPDictionary[name] = ip
print(ip + " is up")
print("Pinged " + str(switchCounter) + " of " + str(len(ipDictionary.keys())) + " total ips")
switchCounter = switchCounter + 1
progress.stop()
popup.destroy()
return validIPDictionary
The problem is when I run the program, I can see that the code stops at the popup.mainloop() line and it does not continue. If I put the popup.mainloop() after the pinging process, the popup appears after the pinging is done, rendering it useless.
How can I make the popup appear while my code is pinging? Is it only by multithreading?

Continuing code after executing a File - Python

I have made a Simple GUI that launches when i run my Twitch TV IRC Bot. But, the main bot doesn't continue until after i close the GUI. How would i make it so the Script runs at the same time as the GUI?
This is the GUI:
##--GUI--##
def whitelist_wipe():
execfile('command_whitelist_wipe.py')
def modpack():
modpack = modpack_input.get()
return
root = Tk()
modpack_input = StringVar()
root.title('Geekster_Bot')
root.geometry('450x450+500+300')
WhitelistButton = Button(root, text = 'Clear Whitelist!', command + whitelist_wipe).pack()
SubmitButton = Button(root, text = 'Change Modpack', command = modpack).pack()
ModpackName = Entry(root, textvariable=modpack_input).pack()
root.mainloop()
This is the part where it launches the GUI from the main Bot scrpit:
try:
execfile('GUI.py')
except:
print('Error loading GUI')
How would i continue the code with the GUI open?
EDIT
GUI.py;
##--GUI--##
def whitelist_wipe():
execfile('command_whitelist_wipe.py')
def modpack_var():
modpack_input = modpack_user.get()
root = Tk()
modpack_user = StringVar()
root.title('Geekster_Bot')
root.geometry('350x100+500+300')
Label1 = Label(root, text = 'Geekster_Bot Controls!').pack()
WhitelistButton = Button(root, text = 'Clear Whitelist!', command =whitelist_wipe).pack(side = LEFT)
SubmitButton = Button(root, text = 'Change Modpack', command = modpack_var).pack()
ModpackName = Entry(root, textvariable=modpack_user).pack()
root.mainloop()
Main Bot;
try:
#Create thread
gui_thread = threading.Thread( target = execfile, args = ('GUI.py',) )
#Start thread
gui_thread.start()
#Code to run in the main thread
except:
print('Error loading GUI')
def message(msg): #function for sending messages to the IRC chat
global queue
queue = queue + 1
print queue
if queue < 20: #ensures does not send >20 msgs per 30 seconds.
twitch = ('OUTPUT ON ' + channel + ' :' + msg + '\r\n')
irc.send('PRIVMSG ' + channel + ' :' + msg + '\r\n')
print (twitch)
else:
print 'Message deleted'
def socialtimer(): #function for announcing social every 3 minutes
global ntimer
z = open(r'E:\Geekster_Bot\Twitter.txt')
SOCIAL = z.read()
message (SOCIAL)
print 'Social Timers Started!'
ntimer = threading.Timer(1200,socialtimer)
ntimer.start()
def queuetimer(): #function for resetting the queue every 30 seconds
global queue
print 'queue reset'
queue = 0
threading.Timer(30,queuetimer).start()
def modpack_var():
modpack_input = modpack_user.get()
##--Main Bot--##
#General variables
newsmsg = 'whitelist'
modpack = modpack_input
Since the gui itself runs in a loop it blocks the thread you start it in until you stop the loop (close the gui). You need to start the gui in a different thread from the main one.
You can do this using the threading module:
import threading
try:
#Create thread
gui_thread = threading.Thread( target = execfile, args = ('GUI.py',) )
#Start thread
gui_thread.start()
#Code to run in the main thread
except:
print('Error loading GUI')
In this code the gui_thread object is a thread, which runs the execfile callable (a function in this case) with the parameter 'GUI.py' on start.
Check the threading module documentation for further info: https://docs.python.org/3.3/library/threading.html

Categories