Python Tkinter entry-point - python

So for a project on my school I am trying to make an GUI where I can add some info for some email so it will send it automatically through an python script.
Now I do have a small problem. I made some entry-points but for some reason I can not add text to the left of the entr-ypoints. Does anyone know how to do that?
I am trying to get 'sending to:', 'name:', and 'course:' to the left of the corresponding entry-points.
It might sounds silly, but please do not change to much of the actual code. It has to be like this for the project.
Kind regards,
Allard
import os
import smtplib
import tkinter as tk
root =tk.Tk()
root.title("Title")
def main():
USER = os.environ.get('USERNAME_GMAIL')
PASSWORD = os.environ.get('PASSWORD_GMAIL')
print("-"*60)
print("\nNew Email\n\n")
with smtplib.SMTP('smtp.gmail.com',587) as smtp:
smtp.ehlo()
smtp.starttls()
smtp.ehlo()
smtp.login(USER, PASSWORD)
PROFESSOR = entry2.get()
COURSE = entry3.get()
subject = 'Course literature: ' + COURSE
body = 'Body of the email, doesnt really matter whats in here' + PROFESSOR
msg= f'Subject:{subject}\n\n{body} '
smtp.sendmail(USER, RECEIVER, msg)
canvas1 = tk.Canvas(root, height=400, width= 400)
canvas1.pack()
entry1=tk.Entry(root)
canvas1.create_window(200, 60, window=entry1)
entry2=tk.Entry(root)
canvas1.create_window(200, 80, window=entry2)
entry3=tk.Entry(root)
canvas1.create_window(200, 100, window=entry3)
def PRINT():
x1 = entry1.get()
x2 = entry2.get()
x3 = entry3.get()
label1= tk.Label(root, text = 'Sending to: ' + str(x1))
canvas1.create_window(200,200, window=label1)
label2= tk.Label(root, text = 'Name: '+ str(x2))
canvas1.create_window(200,220, window=label2)
label3= tk.Label(root, text = 'Course: ' +str(x3))
canvas1.create_window(200,240, window=label3)
button1 = tk.Button(text = 'TEST', bg="black", fg="white", font=('helvetica', 9, 'bold'), command=PRINT)
canvas1.create_window(200,130, window=button1)
button2 = tk.Button(text = 'Verzenden via email', bg = "red", fg= "white", font=('helvetica', 9, 'bold'), command=main)
canvas1.create_window(200,160, window=button2)

You should not be trying to place widgets at exact coordinates. Tkinter has managers for handling layout in a logical manner: pack and grid. These take care of problems with different window sizes, different resolutions, and different fonts, and are superior to placing elements at specific x/y coordinates.
Since you want to create a grid of label/entry pairs, grid is the correct choice.
entry1=tk.Entry(root)
entry2=tk.Entry(root)
entry3=tk.Entry(root)
label1 = tk.Label(root, text="Sending to:")
label2 = tk.Label(root, text="Name:")
label3 = tk.Label(root, text="Course:")
label1.grid(row=0, column=0)
entry1.grid(row=0, column=1)
label2.grid(row=1, column=0)
entry2.grid(row=1, column=1)
label3.grid(row=2 column=0)
entry3.grid(row=2, column=1)

You need to save the item ID of each canvas1.create_window(...) and then use the item ID to find the position of the entry and put a text before the entry using canvas1.create_text(...):
...
# function to put a prompt before the 'entry' item
def create_prompt(prompt, entry):
# get the bounding box of the Entry item
bbox = canvas1.bbox(entry)
# create a canvas text item and put it before the Entry item like below:
# (x-5,y) (x,y)
# -------+ +-----------------+
# prompt: | Entry |
# +-----------------+
#
# note that bbox[0] is the x-coordinate of the top-left of entry item
# bbox[1] is the y-coordinate of the top-left of entry item
# using anchor='ne' means that the (x, y) of the text is the top-right corner of the text
# and finally return the item ID of the canvas text item
return canvas1.create_text(bbox[0]-5, bbox[1], text=prompt, anchor='ne')
entry1=tk.Entry(root)
entry1_item = canvas1.create_window(200, 60, window=entry1)
create_prompt('sending to:', entry1_item)
entry2=tk.Entry(root)
entry2_item = canvas1.create_window(200, 80, window=entry2)
create_prompt('name:', entry2_item)
entry3=tk.Entry(root)
entry3_item = canvas1.create_window(200, 100, window=entry3)
create_prompt('course:', entry3_item)
...

Related

How to enable a disabled Button after filling Entry widgets?

I have 2 Entrys and one button. I want to make that button's state disabled until the two Entrys are filled in. How can I achieve that?
howManyStocksLabel = Label(root, text = "How many stocks do you want to evaluate?")
howManyStocksLabel.grid(row = 1, column = 0)
howManyStocksEntry = Entry(root, borderwidth = 3)
howManyStocksEntry.grid(row = 1, column = 1)
riskLabel = Label(root, text = "Enter risk %")
riskLabel.grid(row = 2, column = 0, sticky = 'w')
riskEntry = Entry(root, borderwidth = 3)
riskEntry.grid(row = 2, column = 1)
nextButton = Button(root, text = "Next!", width = 20, height = 2,state = DISABLED,
fg = 'green', bg = 'white',
command= lambda: myClick(riskEntry, howManyStocksEntry, var))
nextButton.grid(row = 4, column = 1)
I tried to check whether the entries are filled in or not by:
if(riskEntry.get() != ""):
....................
but it just doesn't work.
You need to check if the value is there after the user inputs it. Also, you can use tk.StringVar() as a text variable and trace it.
Here is an example:
import tkinter as tk
def check_entry(*args):
if r1.get() and r2.get():
b1.config(state='normal')
else:
b1.config(state='disabled')
root = tk.Tk()
r1 = tk.StringVar(master=root)
r2 = tk.StringVar(master=root)
e1 = tk.Entry(root, textvariable=r1)
e1.pack()
e2 = tk.Entry(root, textvariable=r2)
e2.pack()
b1 = tk.Button(root, text='Click Me!', state='disabled')
b1.pack()
r1.trace('w', check_entry)
r2.trace('w', check_entry)
root.mainloop()
You will need to use a binding on your entry widgets to check whether the user has entered anything into the entry or not.
This code will fire the check_entry function every time the user types in one of the entry boxes:
riskEntry.bind('<KeyRelease>', check_entry)
howManyStocksEntry.bind('<KeyRelease>', check_entry)
Then your check_entry function might look like this:
def check_entry(event): #event is required for all functions that use a binding
if riskEntry.get() and howManyStocksEntry.get():
nextButton.config(state=NORMAL)
else:
nextButton.config(state=DISABLED)
One way to do it would be to utilize the ability to "validate" their contents that Entry widgets support — see adding validation to an Entry widget — but make it check the contents of multiple Entry widgets and change the state of a Button accordingly.
Below shows how to do this via a helper class that encapsulates most of the messy details needed to make doing it relatively painless. Any number of Entry widgets can be "watched", so it scales well to handle forms consisting of many more than merely two entries.
from functools import partial
import tkinter as tk
from tkinter.constants import *
class ButtonEnabler:
""" Enable/disable a Button depending on whether all specified Entry widgets
are non-empty (i.e. contain at least one character).
"""
def __init__(self, button, *entries):
self.button = button
self.entries = entries
for entry in self.entries:
func = root.register(partial(self.check_entries, entry))
entry.config(validate="key", validatecommand=(func, '%P'))
def check_entries(self, this_entry, new_value):
other_entries = (entry for entry in self.entries if entry is not this_entry)
all_others_filled = all(entry.get() for entry in other_entries)
combined = bool(new_value) and all_others_filled
self.button.config(state=NORMAL if combined else DISABLED)
return True
root = tk.Tk()
howManyStocksLabel = tk.Label(root, text="How many stocks do you want to evaluate?")
howManyStocksLabel.grid(row=1, column=0)
howManyStocksEntry = tk.Entry(root, borderwidth=3)
howManyStocksEntry.grid(row=1, column=1)
riskLabel = tk.Label(root, text="Enter risk %")
riskLabel.grid(row=2, column=0, sticky='w')
riskEntry = tk.Entry(root, borderwidth=3)
riskEntry.grid(row=2, column=1)
nextButton = tk.Button(root, text="Next!", width=20, height=2, state=DISABLED,
fg='green', bg='white', disabledforeground='light grey',
command=lambda: myClick(riskEntry, howManyStocksEntry, var))
nextButton.grid(row=4, column=1)
enabler = ButtonEnabler(nextButton, howManyStocksEntry, riskEntry)
root.mainloop()

Only using grid Tkinter but still getting TclError

So I just started using Google Colab and I keep getting this error:
TclError: cannot use geometry manager grid inside . which already has slaves managed by pack
I'm trying to make a GUI window that takes in the information from the user and saves it.
Everything I've read online says that the issue is that I'm using pack() and grid(), but I'm only using grid(). The error starts when I first try to place my labels (sourceLabel).
I'm so confused, any help would be great.
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
from tkinter import *
except ImportError:
import Tkinter as tk
from Tkinter import *
#creates window
window = tk.Tk()
window.title("File Information")
window.rowconfigure([0,1], minsize=30)
window.columnconfigure([0, 1, 2, 3], minsize=30)
#this program opens the file with information and adds the new information to it
def saveInfo():
value = path.get()
loc = source.get()
recode = recoding.get()
#change name of file and will this make them see everything
#f = open("./info.txt", "a+")
#f.write("Source Data File Location: " + loc + ", Complete File Path: " + value + ", Is recoding of column names and/or values desired?: " + recode)
#f.flush()
#f.seek(0)
#content = f.read()
#print (content)
finalList = [value,loc,recode]
#f.close()
window.withdraw()
print (finalList)
return finalList
#creates a text label, fg=foreground and bg=background, theyre the locations of colors, width and height are measured by text units which are separate horizonatal and vertical
sourceLabel = tk.Label(
text="Source Data File Location:",
width = 21,
height=2)
#adds text to window
sourceLabel.grid(row=0,column=0)
#creates second label
pathLabel = tk.Label(
text="Complete File Path:",
width = 20,
height=2)
#adds text to window
pathLabel.grid(row=1,column=0)
#creates third label
sourceLabel = tk.Label(
text="Is recoding of column \n names and/or values \n desired:",
width = 20,
height=4)
#adds text to window
sourceLabel.grid(row=2,column=0)
#create dropdown for sources
source = StringVar(window)
source.set("Local") # default value
sourceOption = OptionMenu(window, source, "Local", "Google Drive", "One Drive")
sourceOption.grid(row=0,column=1,sticky="ew")
#adds path entry
path = tk.Entry(fg="black", bg="white", width=35)
#adds path to window
path.grid(row=1,column=1,sticky="ew")
#create dropdown for recoding
recoding = StringVar(window)
recoding.set("Yes") # default value
recodingOption = OptionMenu(window, recoding, "Yes", "No")
recodingOption.grid(row=2,column=1,sticky="new")
#creates the click to save button
button = tk.Button(
text="Click to Save",
width=10,
height=1,
bg="white",
fg="black", command=saveInfo
)
#adds Button to window
button.grid(row=4,column=1,sticky="w")
#runs window
window.mainloop()
window.destroy()
This is a very weird error you had here, I just re-wrote your code using the canvas and pack method, instead of a grid, rows, and columns. Just make sure you are using Python3x Just so none of these weird bugs resurface, hope this helps, you can play around with the x and y values at the bottom, and you can mess with the height and width values at the top when we set the canvas. Happy coding!
from tkinter import *
global path, source, recoding # Global Values so that save_info can get the values
# Creates window
root = Tk()
root.title("File Information")
# Canvas Creates the base layer for the window, so instead of l = Label(text="Test").grid(row=2, column=3)
# We would now do l = Label(text="Test")
# canvas.create_window(20, 30, anchor="nw", window=l)
canvas = Canvas(width=400, height=300)
canvas.pack(fill="both", expand=True)
# Canvas.pack() just finishes creating the canvas.
# This program opens the file with information and adds the new information to it.
def save_info():
global path, source, recoding
value = path.get()
loc = source.get()
recode = recoding.get()
# change name of file and will this make them see everything
# f = open("./info.txt", "a+")
# f.write("Source Data File Location: " + loc + ", Complete File Path: " + value + ", Is recoding of column names and/or values desired?: " + recode)
# f.flush()
# f.seek(0)
# content = f.read()
# print (content)
finalList = [value, loc, recode]
# f.close()
root.withdraw()
print(finalList)
return finalList
sourceLabel = Label(
text="Source Data File Location:",
width=21,
height=2)
pathLabel = Label(
text="Complete File Path:",
width=20,
height=2)
recoding_label = Label(
text="Is recoding of column \n names and/or values \n desired:",
width=20,
height=4)
source = StringVar(root)
source.set("Local") # default value
sourceOption = OptionMenu(root, source, "Local", "Google Drive", "One Drive")
path = Entry(fg="black", bg="white", width=35)
recoding = StringVar(root)
recoding.set("Yes") # default value
recodingOption = OptionMenu(root, recoding, "Yes", "No")
button = Button(
text="Click to Save",
width=10,
height=1,
bg="white",
fg="black", command=save_info
)
# Since we are now using canvas, we must add all the elements using canvas.create_window, the first int is the x value, 2nd is the y
# Just leave anchor always as nw, and windows need to equal the variable of the widget they need to add
canvas.create_window(0, 50, anchor="nw", window=sourceLabel)
canvas.create_window(0, 90, anchor="nw", window=pathLabel)
canvas.create_window(0, 140, anchor="nw", window=recoding_label)
canvas.create_window(150, 50, anchor="nw", window=sourceOption)
canvas.create_window(150, 90, anchor="nw", window=path)
canvas.create_window(150, 140, anchor="nw", window=recodingOption)
canvas.create_window(150, 225, anchor="nw", window=button)
root.mainloop()
# I refactored some of the variables so they would be unique

Add data to database using entry in Tkinter

I'm trying to create a GUI that allows to the user to manipulate data from mysql database. So the user must add a trip(trip-id, trip-short-name ...) then add stop times for each trip using Entry from tkinter.
paths=''
new_trip_id=''
def add_trip():
top = Toplevel()
top.title("Trips")
top.geometry("600x300")
lbl1 = Label(top, text = 'service_id:', font = {'Helvetica',10})
lbl1.place(relx=0.2, rely=0.1)
entry1 = Entry(top)
entry1.place(relx=0.45, rely=0.1)
global new_trip_id
lbl2 = Label(top, text = 'trip_id:', font = {'Helvetica',10})
lbl2.place(relx=0.2, rely=0.25)
entry2 = Entry(top)
entry2.place(relx=0.45, rely=0.25)
new_trip_id=entry2.get()
lbl3 = Label(top, text = 'trip_headsign')
entry3 = Entry(top)
entry3.place(relx=0.45, rely=0.4)
lbl4 = Label(top, text = 'trip_short_name:', font{'Helvetica',10})
lbl4.place(relx=0.2, rely=0.55)
entry4 = Entry(top)
entry4.place(relx=0.45, rely=0.55)
entry4.bind('<Return>', add_times)
button1 = Button(top, text="Cancel")
button1.place(relx=0.5 ,rely=0.7)
def add_times(event):
mysql.connector("INSERT INTO trips VALUES service_id, trip_id,
trip_headsign, trip_short_name",
[enty1.get(), entry2.get(), enty3.get(),
enty4.get()])
top = Toplevel()
top.title("Trips")
top.geometry("600x300")
Ididn't found a solution that allows to the user to add data.

Add/delete items from tkinter Listbox permanently

I am trying to create a program that will allow the user to edit the Listbox widget below. I had a (getactive) delete configuration that would visually delete an item from the list. But I had no luck permanently adding or deleting items from the Listbox widget.
Can anybody help me understand how I would configure the Listbox widget to do the above features?
from tkinter import *
modules_list = [
'CLD4002: Introduction to Operating Systems Virtualisation',
'CLD4003: Linux Essentials',
'SEC4001: Introduction to Networking',
'SEC4002: Routing Fundamentals',
'SEC4003: Security Fundamentals',
'SWE4001: Introduction to Software Development',
'CLD5005: Advanced Linux',
'SEC5001: Computing Security',
'SEC5002: Network Architecture',
'SEC5003: Wide Area Networks',
'SEC5005: Enterprise Infrastructure',
'HE5: CHOSEN OPTIONAL MODULE',
'CLD6000: Contemporary Problems Analysis',
'CDL6001: Undergraduate Research Project',
'SEC6003: Operations Management',
'SEC6004: Cloud and Network Security',
'HE6: CHOSEN OPTIONAL MODULE'
]
entries=[]
AVERAGE_TOT = 0 # global variable
CLASSIFICATION = "not Classified" # global variable
def print_Listbox():
z = listbox.get(0, END)
print (z)
# YEAR ONE LABELS
y1 = Label (right_frame, text="Enter Grade")
y1.grid(row=1, column=4)
row_offset = 0+2
for module in modules_list:
#Create labesl from modules_list
lbl = Label(right_frame, text=module)
lbl.grid(row=row_offset, column=3)
mod_code = module[:7] # splitting the string at the 7th character from the beginint
# create entry fields based on number of modules in modules_list
ent= Entry(right_frame, textvariable=mod_code)
ent.grid(row=row_offset, column=4)
entries.append(ent)
row_offset+=1
classification = Label (right_frame, text="Your degree classification is :" + CLASSIFICATION)
average_result = Label (right_frame, text="Your average is " + str(AVERAGE_TOT))
# FINAL AWARD CONFIGURATIONS
classification.grid(row=len(modules_list)+2, column=4)
average_result.grid(row=len(modules_list)+3, column=4)
b1 = Button (right_frame, text="press", command=lambda: setAverage(classification,average_result))
b1.grid(row=len(modules_list)+4, column=4)
def setAverage(classification, average_result):
total = 0
for entry in entries:
thisent = entry.get()
total += int(thisent)
average = total / len(entries)
if average <=39:
degreeclass = "fail"
if average >=40 and average <=49:
degreeclass = "3rd"
if average >=50 and average <=59:
degreeclass = "2:2"
if average >=60 and average <=69:
degreeclass = "2:2"
if average >=70:
degreeclass = "1st"
average_result.config(text="Your percentage is :" + str(average))
classification.config(text="Your degree classification is :" + degreeclass)
main = Tk()
var = StringVar
left_frame = Frame(main)
left_frame.grid(row=0, column=0)
middle_frame = Frame(main)
middle_frame.grid(row=0, column=1)
right_frame = Frame(main)
right_frame.grid(row=0, column=2)
l1 = Label(left_frame, text="Search")
l1.grid(row=0, column=0)
listbox = Listbox(left_frame, font = ("Purisa", 10, "bold"), height=20, width=55)
for i in modules_list:
listbox.insert(END, i)
listbox.grid(rowspan=10)
all_items = listbox.get(0, END)
b1 = Button(middle_frame, text="Add", font = ("Purisa", 10, "bold"))
b1.grid(row=3, column=1, columnspan=1)
b2 = Button(middle_frame, text="Print", font = ("Purisa", 10, "bold"), command=print_Listbox)
b2.grid(row=4, column=1, columnspan=1)
b3 = Button(middle_frame, text="Delete", font = ("Purisa", 10, "bold"))
b3.grid(row=5, column=1, columnspan=1)
main.mainloop()
You can simply use index = listbox.curselection() to get the selected item in the listbox and then use listbox.delete(index) to remove the item from the listbox.
To add new item to listbox, you need to get the item using any input method, for example tkinter.simpledialog, and then use listbox.insert('end', item) to append the item to the listbox.
Therefore, define two new functions for adding and deleting item from listbox:
from tkinter import simpledialog
def add_item():
item = simpledialog.askstring("Input", "Enter item name:")
if item is not None:
listbox.insert('end', item)
def delete_item():
index = listbox.curselection()
listbox.delete(index)
Then modify Add and Delete buttons to call the corresponding function.
BTW, there are issues in your print_Listbox function:
for module in modules_list: should be for module in z:
mod_code = module[:7] # splitting the string at the 7th character from the beginint should be mod_code = module.split(':')[0] because the mod_code in your list are not all 7 characters long.

How to clear part of a tkinter Canvas and show something when submit is pressed?

I'm creating a simple madlib style game and I've come into a bit of a problem. I cannot get the canvas to clear and show the results.
The following code places an image as the background of a canvas. It then places labels and entry fields in 2 columns for all of the words to be inserted. There is a submit button at the bottom of the page. I can't figure out how to get it clear everything except the background image, so that it can display the story, with the users words inserted. If i place it in the callback(), it clears just the background and keeps everything else. I want the opposite.
from tkinter import *
from PIL import Image, ImageTk
canvas_width = 360
canvas_height = 525
file = r"C:\Users\kraak\Desktop\PyCharm Community Edition 2017.1.2\borderedpaper.GIF"
master = Tk()
canvas = Canvas(master, width=canvas_width, height=canvas_height)
old_img = PhotoImage(file=file)
new_img = old_img.subsample(3, 3)
canvas.create_image(-11, -10, anchor=NW, image=new_img)
canvas.create_window(0, 0, height=1, width=1, anchor=NW)
canvas.create_text(0, 0, text="Test")
e1 = Entry(canvas)
canvas.create_window(250, 60, window=e1, height=15, width=100)
label = Label(text="Enter an adjective.")
label.place(x=40, y=50)
e1.focus_set()
e2 = Entry(canvas)
canvas.create_window(250, 85, window=e2, height=15, width=100)
label = Label(text="Enter a nationality.")
label.place(x=40, y=75)
e2.focus_set()
def callback():
print("Pizza was invented by a " + (e1.get()) + " " + (e2.get()))
def answer():
button = Button(text="Submit.", command=callback)
button.place(x=150, y=460)
answer()
canvas.pack()
mainloop()
As Bryan Oakley suggested you can store the id's of the widgets you want to get rid of in a list to make it easier to destroy() them all in the callback() function. Here's showing the modification to your code that would do that—note the lines with a # ADDED comments.
from tkinter import *
from PIL import Image, ImageTk
canvas_width = 360
canvas_height = 525
file = r"C:\Users\kraak\Desktop\PyCharm Community Edition 2017.1.2\borderedpaper.GIF"
master = Tk()
canvas = Canvas(master, width=canvas_width, height=canvas_height)
canvas_entry_widgets = [] # ADDED
old_img = PhotoImage(file=file)
new_img = old_img.subsample(3, 3)
canvas.create_image(-11, -10, anchor=NW, image=new_img)
canvas.create_window(0, 0, height=1, width=1, anchor=NW)
canvas.create_text(0, 0, text="Test")
e1 = Entry(canvas)
canvas.create_window(250, 60, window=e1, height=15, width=100)
label = Label(text="Enter an adjective.")
label.place(x=40, y=50)
e1.focus_set()
canvas_entry_widgets.append(e1) # ADDED
e2 = Entry(canvas)
canvas.create_window(250, 85, window=e2, height=15, width=100)
label = Label(text="Enter a nationality.")
label.place(x=40, y=75)
e2.focus_set()
canvas_entry_widgets.append(e2) # ADDED
def callback():
print("Pizza was invented by a " + (e1.get()) + " " + (e2.get()))
# destroy the canvas entry widgets and clear the list # ADDED
while canvas_entry_widgets: # ADDED
widget = canvas_entry_widgets.pop() # ADDED
widget.destroy() # ADDED
def answer():
button = Button(text="Submit.", command=callback)
button.place(x=150, y=460)
answer()
canvas.pack()
mainloop()
Every widget has a destroy method which can be used to delete the widget. In your callback you can simply call this method for every widget:
def callback():
e1.destroy()
e2.destroy()
...
In your specific case, if you want to delete all the labels you will have to give them unique names. Or, to make this even easier, you can store all of your widgets and iterate over the list.

Categories