Turtle GUI improvement - python

I'm a 11th grader (5th year student) that just started studying Python programming. I am trying to make a GUI where I can chose what shape I want to draw with turtle and input the values needed to draw out the shape. I managed to do that with the following code (where I already did the functions of the turtle fractals and imported that file):
from tkinter import *
import turtlegraphics
from turtle import *
import math
#set up the window
root = Tk()
root.title('Turtle shapes')
root.geometry('300x200+100+100')
#make the interface
turtleLabel = Label(root, text = 'Turtle Fractals Generator')
turtleLabel.grid(row = 0, column =1, columnspan =2)
#order widgets
orderLabel = Label(root, text ='Order')
orderLabel.grid(row = 1, column =0)
orderStr = StringVar()
orderEntry = Entry(root, textvariable = orderStr)
orderEntry.grid(row = 1, column = 1, columnspan = 2)
#length widgets
lengthLabel = Label(root, text ='Length')
lengthLabel.grid(row = 2, column =0)
lengthStr = StringVar()
lengthEntry = Entry(root, textvariable = lengthStr)
lengthEntry.grid(row = 2, column = 1, columnspan = 2)
#button widgets
def clearF():
#Clear the entries
orderStr.set('')
lengthStr.set('')
return
#end def
clearButton = Button(root, text ='Clear', command = clearF)
clearButton.grid(row = 3, column =1)
def treeF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.tree(order,length, pen)
return
#end def
treeButton = Button(root, text ='Tree', command = treeF)
treeButton.grid(row = 1, column =3)
#make a gasket button
def gasketF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.gasket(order,length,pen)
return
#end def
gasButton = Button(root, text ='Gasket', command = gasketF)
gasButton.grid(row = 2, column =3)
#make a dandelion button
def dandelionF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.tree4(order,length,pen)
return
#end def
danButton = Button(root, text ='Dandelion', command = dandelionF)
danButton.grid(row = 3, column =3)
#make flalke button
def kevinF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.k(order,length,pen)
return
#end def
def flakeF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.flake(order,length,pen)
return
#end def
flakeButton = Button(root, text ='Flake', command = flakeF)
flakeButton.grid(row = 4, column =3)
#make a screen and a pen
pen = Pen()
screen = Screen()
pen.speed(0)
pen.color('blue')
pen.width(3)
screen.bgcolor('white')
root.mainloop()
However, I want to try and put a list to chose the shape from rather than having several buttons. I don't understand much about lists due to the fact that I wasn't taught it yet but after doing research this is what I got:
'''
program gui to draw turtle fractals
'''
from tkinter import *
import turtlegraphics
from turtle import *
import math
#make a screen and a pen
pen = Pen()
def treeF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.tree(order,length, pen)
return
#end def
#make a gasket button
def gasketF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.gasket(order,length,pen)
return
#end def
#make a dandelion button
def dandelionF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.tree4(order,length,pen)
return
#end def
#make flalke button
def kevinF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.k(order,length,pen)
return
#end def
def flakeF():
order = int(orderStr.get())
length = float(lengthStr.get())
turtlegraphics.flake(order,length,pen)
return
#end def
screen = Screen()
pen.speed(0)
pen.color('blue')
pen.width(3)
screen.bgcolor('white')
#set up the window
root = Tk()
root.title('turtle shapes')
root.geometry('600x300+100+100')
#make the interface
turtleLabel = Label(root, text = 'Turtle Fractals Generator')
turtleLabel.grid(row = 0, column =1, columnspan =2)
#order widgets
orderLabel = Label(root, text ='Order')
orderLabel.grid(row = 1, column =0)
orderStr = StringVar()
orderEntry = Entry(root, textvariable = orderStr)
orderEntry.grid(row = 1, column = 1, columnspan = 2)
#length widgets
lengthLabel = Label(root, text ='Length')
lengthLabel.grid(row = 2, column =0)
lengthStr = StringVar()
lengthEntry = Entry(root, textvariable = lengthStr)
lengthEntry.grid(row = 2, column = 1, columnspan = 2)
#button widgets
def clearF():
#Clear the entries
orderStr.set('')
lengthStr.set('')
return
#end def
clearButton = Button(root, text ='Clear', command = clearF)
clearButton.grid(row = 3, column =1)
#list
def selection():
obj = nameList.curselection()[0]
if obj == 'tree':
command = treeF
return
if obj == 'dandelion':
command = dandelionF
return
if obj == 'flake':
command = flakeF
return
if obj == 'gasketF':
command = gasketF
return
#end if
#end def
#create list
listbox = Listbox(root,selectmode = SINGLE) #select mode is type of
selection
listButton= Button(root, text = 'chose fractal')
labelVar = StringVar()
nameLabel = Label(root, textvariable = labelVar)
nameLabel.grid(row = 2 , column = 7, columnspan = 2)
labelVar.set('choose a fractal')
names = ['tree','dandelion','flake','gasket']
nameList = Listbox(root)
for i in names:
nameList.insert(END, i)
nameList.grid(row = 4, column =7, columnspan = 2)
selectionButton = Button(root, text = 'draw', command = selection)
selectionButton.grid(row = 5, column = 7, columnspan = 2)
root.mainloop()
I know something is wrong with my code, but I can't tell what I did wrong. When I run my code I get no error whatsoever, but it does not draw the shape. In addition, the clear button does not do its function when clicked. How can I can I fix this?

Your immediate problem is how selection() works -- it was comparing numbers to strings and have the code necessary to execute any of the subprograms. I've reworked it below to get the behavior you desire. I've also made other changes and simplifications:
from tkinter import *
from turtle import Pen, Screen
import turtlegraphics
def treeF():
order = int(orderEntry.get())
length = float(lengthEntry.get())
turtlegraphics.tree(order, length, pen)
def dandelionF():
order = int(orderEntry.get())
length = float(lengthEntry.get())
turtlegraphics.tree4(order, length, pen)
def flakeF():
order = int(orderEntry.get())
length = float(lengthEntry.get())
turtlegraphics.flake(order, length, pen)
def gasketF():
order = int(orderEntry.get())
length = float(lengthEntry.get())
turtlegraphics.gasket(order, length, pen)
def kevinF():
order = int(orderEntry.get())
length = float(lengthEntry.get())
turtlegraphics.k(order, length, pen)
fractals = [('tree', treeF), ('dandelion', dandelionF), ('flake', flakeF), ('gasket', gasketF), ('kevin', kevinF)]
# make a screen and a pen
screen = Screen()
screen.bgcolor('white')
pen = Pen()
pen.speed('fastest')
pen.color('blue')
pen.width(3)
# set up the window
root = Tk()
root.title('turtle shapes')
root.geometry('600x400+100+100')
# make the interface
turtleLabel = Label(root, text='Turtle Fractals Generator')
turtleLabel.grid(row=0, column=1, columnspan=2)
# order widgets
orderLabel = Label(root, text='Order')
orderLabel.grid(row=1, column=0)
orderEntry = Entry(root)
orderEntry.grid(row=1, column=1, columnspan=2)
# length widgets
lengthLabel = Label(root, text='Length')
lengthLabel.grid(row=2, column=0)
lengthEntry = Entry(root)
lengthEntry.grid(row=2, column=1, columnspan=2)
# button widgets
def clearF():
# Clear the entries
orderEntry.delete(0, 'end')
lengthEntry.delete(0, 'end')
clearButton = Button(root, text='Clear', command=clearF)
clearButton.grid(row=3, column=1)
# list
def selection():
selections = nameList.curselection()
if selections:
selection = selections[0]
if selection < len(fractals):
name, definition = fractals[selection]
definition()
# create list
listbox = Listbox(root, selectmode=SINGLE) # select mode is type of selection
listButton = Button(root, text='chose fractal')
labelVar = StringVar()
nameLabel = Label(root, textvariable=labelVar)
nameLabel.grid(row=2, column=7, columnspan=2)
labelVar.set('choose a fractal')
nameList = Listbox(root)
for name, definition in fractals:
nameList.insert(END, name)
nameList.grid(row=4, column=7, columnspan=2)
selectionButton = Button(root, text='Draw', command=selection)
selectionButton.grid(row=5, column=7, columnspan=2)
root.mainloop()
General comments about your code:
Find and follow a good Python coding style -- it makes understanding your code more difficult when you invent your own coding "style".
This isn't the correct way to integrate turtle with tkinter. Search StackOverflow, or the turtle documentation, for RawTurtle and TurtleScreen, the classes you use instead of Turtle (aka Pen) and Screen when embedding in an existing Tk window structure.

Related

tkinter throws error using a variable for button name to set state

I want to pass a button name to tkinter as a variable. This method works for Text fields, but not for buttons in my code.
I am building a gui app and I want to have a generic clear function that zaps a text entry field and resets a button from NORMAL to DiSABLED.
There are multiple buttons and fields, hence the desire to make this generic.
For the code I have, the buttons are present with the exception of clear all.
I am setting the variable w_button to the specific name of the (existing) button based on what is passed to the function.
def switch_clear(elem_type):
if elem_type == 'scn':
w_button = 'b_clear_scn'
clear_field = 'scn_file_entry'
print ('scenario')
elif elem_type == 'sol2':
w_button = 'b_clear_sol2'
clear_field = 'sol2_file_entry'
print ('sol2')
elif elem_type == 'mdl':
w_button = 'b_clear_mdlList'
clear_field = 'mdlList_file_entry'
print ('mdl')
elif elem_type == 'all':
print ('clear all TBD')
return()
if w_button["state"] == NORMAL:
clear_field.delete(0, END)
w_button["state"] = DISABLED
return()
Here's what happens:
C:\utils>my_frame3.py
scenario
Traceback (most recent call last):
File "C:\utils\my_frame3.py", line 127, in <module>
b_clear_scn = Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
File "C:\utils\my_frame3.py", line 100, in switch_clear
if w_button["state"] == NORMAL:
TypeError: string indices must be integers
C:\utils>
I realize I could duplicate and push the clear operations into the if/elif statements, and I may have to live with that but - is it possible to reference buttons as variables? How can I do this?
Complete code as requested below. The preview is a mess despite using the code widget, but in my file lines are correctly formatted (notepad++). Tx
import os, sys
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
root2 = Tk()
root2.title('Model Processing')
root2.geometry('{}x{}'.format(512, 400))
# colors
color1 = 'light cyan'
color2 = 'gold'
color3 = 'RosyBrown1'
color4 = 'lavender'
color5 = 'linen'
bg_color = 'azure'
# number of items to process; until >1 OK is disabled; if >1 OK is enabled TBD
l_to_do = [] # items to process; scn, sol2, modelList, installed models
# functions/commands
def callback():
print ('A button was clicked.')
return;
def my_exit():
result = messagebox.askquestion("Cancel Model Processing", "Are you sure?", icon='warning')
if result == 'yes':
root2.quit()
else: # user changed mind
pass
def choose_import_file(str_ftype):
which_field = ''
which_clear_btn = ''
ftype = str_ftype
mdl_opts = {}
if ftype == 'scn':
title_msg = 'Choose a scenario file'
mdl_opts['filetypes'] = [('Supported types', ('.scn')),
('scenario files',('.scn'))]
which_field = scn_file_entry
which_clear_btn = 'b_clear_scn'
elif ftype == 'sol2':
title_msg = 'Choose a SOL2 file'
mdl_opts['filetypes'] = [('Supported types', ('.txt')),
('sol2 files',('.txt'))]
which_field = sol2_file_entry
elif ftype == 'mdllist':
title_msg = 'Choose a ModelList file'
print ('TBD: ModelList file')
file_input = filedialog.askopenfilename(title = title_msg, **mdl_opts)
if file_input == '':
print ('error or cancelled by user')
return
else:
f_inp_file = os.path.basename(file_input)
f_inp_file_base = str(file_input.split('.')[0])
f_inp_file_ext = str.lower(str(file_input.split('.')[1]))
f_inp_d_name = os.path.dirname(file_input)
print('File chosen:', f_inp_file_base)
# populate scenario file field
which_field.insert(INSERT,file_input)
which_clear_btn["state"] = NORMAL
# define appropriate clear button active
# define_clear_btn.configure(state = ACTIVE)
return
def switch_clear(elem_type):
if elem_type == 'scn':
if b_clear_scn["state"] == NORMAL:
scn_file_entry.delete(0, END)
b_clear_scn["state"] = DISABLED
elif elem_type == 'sol2':
f b_clear_sol2["state"] == NORMAL:
clear_field = 'sol2_file_entry'
b_clear_sol2["state"] = DISABLED
elif elem_type == 'mdl':
if b_clear_mdlList["state"] == NORMAL:
clear_field = 'mdlList_file_entry'
b_clear_mdlList["state"] = DISABLED
elif elem_type == 'all':
print ('clear all TBD')
return()
return()
# create all of the main containers
first_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
second_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
third_frame = Frame(root2, bg=color5, width=512, height=90, pady=10)
fourth_frame = Frame(root2, bg=color5, width = 512, height = 90, pady=10)
# layout all of the main containers
root2.grid_rowconfigure(3, weight=1)
root2.grid_rowconfigure(2, weight=1)
root2.grid_rowconfigure(1, weight=1)
root2.grid_columnconfigure(0, weight=1)
first_frame.grid(row=0, sticky="ew")
second_frame.grid(row=1, sticky="ew")
third_frame.grid(row=2, sticky="ew")
fourth_frame.grid(row = 3, sticky="e")
# create the widgets for the first frame
#scn_label = Label(first_frame, text = 'Scenario file')
scn_file_entry = Entry(first_frame, background=bg_color, width = 50)
b_choose_scn = Button(first_frame, text = "Choose a scenario..", command = lambda: choose_import_file('scn'), height = 2)
b_clear_scn = Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
# layout the widgets in the first frame
#scn_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
scn_file_entry.grid(row = 0, column = 1, padx = (10,10))
b_choose_scn.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_scn.grid(row=2, column=0, padx = (10,10), sticky=W)
# second frame
# sol2_label = Label(second_frame, text = 'Sol2 file')
sol2_file_entry = Entry(second_frame, background=bg_color, width = 50)
b_choose_sol2 = Button(second_frame, text = "Choose SOL2 file..", command = lambda: choose_import_file('sol2'), height = 2)
b_clear_sol2 = Button(second_frame, text = "Clear SOL2", command = switch_clear('sol2'), height = 2, state=DISABLED)
# layout the widgets in the second frame
# sol2_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
sol2_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_sol2.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_sol2.grid(row=2, column=0, padx = (10,10), sticky=W)
# third frame
# mdlList_label = Label(third_frame, text = 'ModelList.txt file')
mdlList_file_entry = Entry(third_frame, background=bg_color, width = 50)
b_choose_mdlList = Button(third_frame, text = "Choose ModelList.txt file..", command = callback, height = 2)
b_clear_mdlList = Button(third_frame, text = "Clear ModelList", command = callback, height = 2, state=DISABLED)
# layout the widgets in the third frame
#mdlList_label.grid(row = 0, column = 0, padx = (10,10), pady=5, sticky = 'ns')
mdlList_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_mdlList.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_mdlList.grid(row=2, column=0, padx = (10,10), sticky=W)
#####################################################################
# create bottom widgets
#####################################################################
clear_all = Button(fourth_frame, text='Clear All', padx = '5', command = callback, height = 2, state=DISABLED)
ok_btn = Button(fourth_frame, text='OK', padx = '5', command = callback, height = 2, state=DISABLED)
cancel_btn = Button(fourth_frame, text='Cancel', height = 2, padx = '12', command = my_exit)
#####################################################################
# layout the bottom widgets
#####################################################################
clear_all.grid(row = 0, column = 2, sticky = 'e')
ok_btn.grid(row = 0, column = 3, sticky = 'e')
cancel_btn.grid(row = 0, column = 4, sticky = 'e')
# commands/bindings
root2.mainloop()
I believe this is what you are trying to do. The below is fully commented with explanations. switch_clear becomes entirely unnecessary with this method. The buttons directly clear their corresponding entry in their command, and Entry validation takes care of the corresponding button state.
Note: I wrote all of this before you posted your full code
import tkinter as tk
root = tk.Tk()
root.geometry('400x400')
#toggle the state of buttons based on entry text length
def toggle_button(name, text):
global btn_switch
btn_switch[name]['state'] = 'normal' if len(text) else 'disabled'
return True
#to hold the buttons so they are easy to position
#just for this example
buttons = tk.Frame(root)
buttons.pack(side='top', anchor='nw')
#create a tcl wrapper for the validate command
vcmd = tk.Widget.register(root, toggle_button)
#mock-up of your entries ~ validate on key press. send widget name and full text to vcmd
scn_file_entry = tk.Entry(root, width=20)
scn_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
scn_file_entry.pack(side='left', anchor='nw')
sol2_file_entry = tk.Entry(root, width=20)
sol2_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
sol2_file_entry.pack(side='left', anchor='nw')
mdlList_file_entry = tk.Entry(root, width=20)
mdlList_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
mdlList_file_entry.pack(side='left', anchor='nw')
#mock-up of your buttons ~ delete the entry text in a lambda and let entry validation handle the button state
b_clear_scn = tk.Button(buttons, text="scn", state='disabled')
b_clear_scn.configure(command=lambda: scn_file_entry.delete(0, 'end'))
b_clear_scn.pack(side='left', anchor='nw')
b_clear_sol2 = tk.Button(buttons, text="sol2", state='disabled')
b_clear_sol2.configure(command=lambda: sol2_file_entry.delete(0, 'end'))
b_clear_sol2.pack(side='left', anchor='nw')
b_clear_mdlList = tk.Button(buttons, text="mdl", state='disabled')
b_clear_mdlList.configure(command=lambda: mdlList_file_entry.delete(0, 'end'))
b_clear_mdlList.pack(side='left', anchor='nw')
#create a dictionary of 'widget name':corresponding button, for toggle_button to reference
btn_switch = {
f'{scn_file_entry}':b_clear_scn,
f'{sol2_file_entry}':b_clear_sol2,
f'{mdlList_file_entry}':b_clear_mdlList,
}
root.mainloop()

Random tkinter window opening on if/else statement

I'm wondering if I got my if else statement wrong or if its a tkinter issue. I want it so that if a 0 is left in any or all boxes, it gives an error message. But after the error message is closed, it opens a random blank window. This is my code. The specific area is the if else statement within the function valueget()
import tkinter as tk
def mainwindow():
mainwindow = tk.Tk()
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
tk.Label(mainwindow, text = 'Enter a', font = ('verdana'), bg='#aaf0d1').grid(row=0)
tk.Label(mainwindow, text = 'Enter b', font = ('verdana'), bg='#aaf0d1').grid(row=1)
tk.Label(mainwindow, text = 'Enter c', font = ('verdana'), bg='#aaf0d1').grid(row=2)
getA = tk.IntVar()
aBox = tk.Entry(mainwindow, textvariable = getA, width=3, bg='#aaf0d1')
aBox.grid(row=0, column=1)
aBox.config(highlightbackground='#aaf0d1')
getB = tk.IntVar()
bBox = tk.Entry(mainwindow, textvariable = getB, width=3, bg='#aaf0d1')
bBox.grid(row=1, column=1)
bBox.config(highlightbackground='#aaf0d1')
getC = tk.IntVar()
cBox = tk.Entry(mainwindow, textvariable = getC, width=3, bg='#aaf0d1')
cBox.grid(row=2, column=1)
cBox.config(highlightbackground='#aaf0d1')
button = tk.Button(mainwindow, text='Obtain roots', command = lambda: valueget(), font = ('verdana'), highlightbackground='#aaf0d1')
button.grid(row=4)
button.config(bg='#aaf0d1')
def valueget():
readA = getA.get()
readB = getB.get()
readC = getC.get()
intA = int(readA)
intB = int(readB)
intC = int(readC)
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
messagewindow = tk.Tk()
messagewindow.geometry('290x50')
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
if readA == 0 or readB==0 or readC==0 or (readA==0 and readB==0 and readC==0):
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
messagewindow.mainloop()
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
mainwindow.mainloop()
def startup():
startpage = tk.Tk()
startpage.title('Solver')
photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage, image = photo, command = lambda: [startpage.destroy(), mainwindow()])
coverbutton.pack()
coverbutton.configure(highlightbackground='#aaf0d1')
startpage.mainloop()
startup()
Here's a basic idea of what I would do:
import tkinter as tk
from tkinter import messagebox
def mainwindow(root):
# Creates a toplevel window
mainwindow = tk.Toplevel()
mainwindow.protocol("WM_DELETE_WINDOW", root.destroy) # This overrides the "X" being clicked to also destroy the root window.
root.withdraw() # "Hides" the root window, leaving it (and mainloop) running in the background.
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
# Since all three of the labels/entries are the same
# we can save space by generating them in a loop
entry_items = ('Enter a', 'Enter b', 'Enter c')
values = []
for x, item in enumerate(entry_items): # Using enumerate and x to assign rows
tk.Label(mainwindow, text = item,
font = ('verdana'), bg='#aaf0d1').grid(row=x) # Row assigned to x.
values.append(tk.StringVar()) # Appended StringVar to list.
tk.Entry(mainwindow,
textvariable = values[-1], # Uses the last value appended to the values list.
highlightbackground='#aaf0d1',
width=3,
bg='#aaf0d1').grid(row=x, column=1) # Row assigned to x.
tk.Button(mainwindow,
text='Obtain roots',
command = lambda vals = values: valueget(vals), # Here the button command is assigned with the values list
font = ('verdana'), bg='#aaf0d1',
highlightbackground='#aaf0d1').grid(row=3) # we know there are 3 items before this.
mainwindow.lift() # This is a method of bringing a window to the front
def valueget(vals):
# This line gets the values from the StringVars, converts them to ints,
# and returns them to their respective variables.
try:
readA, readB, readC = [int(val.get()) for val in vals]
except ValueError:
messagebox.showerror(title="Number Error", message='Values must be numbers')
return
# Here the variables are checked to see if they are 0
# Since each one is being checked if it is 0, there is no need to check if they are all 0.
for val in (readA, readB, readC):
if val == 0:
# If they are 0, shows an error message
messagebox.showerror(title="Zero Error", message='Values must not be zero')
return
# Creates a toplevel to display the results
messagewindow = tk.Toplevel()
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
tk.Label(messagewindow,
text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}',
bg='#aaf0d1',
font = ('verdana')).pack(padx = 5, pady = 2)
tk.Button(messagewindow,
text='Close',
command = messagewindow.destroy, # There is no need for a lambda for this.
font = ('verdana'),
bg = '#aaf0d1',
highlightbackground='#aaf0d1').pack(padx = 5, pady = 2)
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
messagewindow.lift() # This is a method of bringing a window to the front
def startup():
startpage = tk.Tk()
startpage.title('Solver')
# COMMENTED OUT FOR TESTING
#photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage,
# COMMENTED OUT FOR TESTING
#image = photo,
text = "TESTING", # HERE FOR TESTING
highlightbackground='#aaf0d1',
command = lambda root = startpage: mainwindow(root)).pack() # Passes the startpage to the mainwindow function.
startpage.mainloop() # The only mainloop you need.
startup()
I would recommend to improve the readability of the if-else statement for a start.
coefficients = [readA, readB, readC]
if sum(coefficients): # If they all are all zeros this will be False
if min(coefficients): # If any one is zero, this will be False
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
else:
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
errorwindow = tk.messagebox.showerror(message='none').pack()

tkinter, Image does not show up unless some function added

I have written a code for a basic game but the image and shape don't show up unless I add something like item.pack() or win.mainloop() [which doesn't really make sense] but then the lines below it don't run.
When I don't have anything, the buttons show up but the image doesn't show up.
import tkinter as tk
import random
from tkinter import messagebox
win = tk.Tk()
my_label = tk.Label(win, text="Color of the Baloon Game")
my_label.pack()
my_canvas = tk.Canvas(win, width=400, height=600)
my_canvas.pack()
background_image=tk.PhotoImage(file = "CS_Game_menu.png")
background_label = tk.Label(my_canvas, image=background_image)
background_label.photo = background_image
background_label.grid(row = 0, rowspan = 10, column = 0, columnspan = 10)
def drawCircle():
color = "green"
x1 = 265
y1 = 80
diameter = 90
my_canvas.destroy()
circle_button.destroy()
quit_button.destroy()
my_label.destroy()
my_label1 = tk.Label(win, text="What is the Color of the Baloon?", font="Purisa")
my_label1.pack()
my_canvas1 = tk.Canvas(win, width=400, height=600)
my_canvas1.pack()
image1 = r"CS_Game_baloon.png"
photo1 = tk.PhotoImage(file=image1)
item = my_canvas1.create_image(200, 350, image=photo1)
shape = my_canvas1.create_oval(x1, y1, x1 + diameter, y1 + diameter+20, fill=color)
item.pack()
game1_button = tk.Button(my_canvas1, text = "Green")
game1_button.grid(row= 8, column = 3)
game1_button["command"] = lambda: messagebox.showinfo("Congratulations!", "Correct Answer!")
game2_button = tk.Button(my_canvas1, text = "Blue")
game2_button.grid(row= 8, column = 5)
game2_button["command"] = lambda: messagebox.showinfo("Sorry!", "Incorrect Answer!")
game3_button = tk.Button(my_canvas1, text = "Red")
game3_button.grid(row= 8, column = 7)
game3_button["command"] = lambda: messagebox.showinfo("Sorry", "Incorrect Answer!")
circle_button = tk.Button(win, text="New Game")
circle_button.pack()
circle_button["command"] = drawCircle
quit_button = tk.Button(win, text="Quit")
quit_button.pack()
quit_button['command'] = win.destroy
You are using both the create_... methods and grid methods on your canvas object. It won't behave as you expected.
To achieve what you want, you can create a Frame, put your buttons in it, and then use create_window method on your canvas:
def drawCircle():
...
shape = my_canvas1.create_oval(x1, y1, x1 + diameter, y1 + diameter+20, fill=color)
frame = tk.Frame(my_canvas1)
game1_button = tk.Button(frame, text = "Green")
game1_button.grid(row= 8, column = 3)
game1_button["command"] = lambda: messagebox.showinfo("Congratulations!", "Correct Answer!")
game2_button = tk.Button(frame, text = "Blue")
game2_button.grid(row= 8, column = 5)
game2_button["command"] = lambda: messagebox.showinfo("Sorry!", "Incorrect Answer!")
game3_button = tk.Button(frame, text = "Red")
game3_button.grid(row= 8, column = 7)
game3_button["command"] = lambda: messagebox.showinfo("Sorry", "Incorrect Answer!")
my_canvas1.create_window(200,500,window=frame)
And of course, add win.mainloop() to the bottom of your program if you haven't already.

How to display output of print() in GUI python

I am new in creating GUI. I am doing it in Python with Tkinter. In my program I calculate following characteristics
def my_myfunction():
my code ...
print("Centroid:", centroid_x, centroid_y)
print("Area:", area)
print("Angle:", angle)
I would like to ask for any help/tips how to display those values in GUI window or how to save them in .txt file so that I can call them in my GUI
Thanks in advance
Tkinter is easy and an easy way to do a GUI, but sometimes it can be frustrating. But you should have read the docs before.
However, you can do in this way.
from tkinter import *
yourData = "My text here"
root = Tk()
frame = Frame(root, width=100, height=100)
frame.pack()
lab = Label(frame,text=yourData)
lab.pack()
root.mainloop()
There are several ways to display the results of any operation in tkiner.
You can use Label, Entry, Text, or even pop up messages boxes. There are some other options but these will probably be what you are looking for.
Take a look at the below example.
I have a simple adding program that will take 2 numbers and add them together. It will display the results in each kind of field you can use as an output in tkinter.
import tkinter as tk
from tkinter import messagebox
class App(tk.Frame):
def __init__(self, master):
self.master = master
lbl1 = tk.Label(self.master, text = "Enter 2 numbers to be added \ntogether and click submit")
lbl1.grid(row = 0, column = 0, columnspan = 3)
self.entry1 = tk.Entry(self.master, width = 5)
self.entry1.grid(row = 1, column = 0)
self.lbl2 = tk.Label(self.master, text = "+")
self.lbl2.grid(row = 1, column = 1)
self.entry2 = tk.Entry(self.master, width = 5)
self.entry2.grid(row = 1, column = 2)
btn1 = tk.Button(self.master, text = "Submit", command = self.add_numbers)
btn1.grid(row = 2, column = 1)
self.lbl3 = tk.Label(self.master, text = "Sum = ")
self.lbl3.grid(row = 3, column = 1)
self.entry3 = tk.Entry(self.master, width = 10)
self.entry3.grid(row = 4, column = 1)
self.text1 = tk.Text(self.master, height = 1, width = 10)
self.text1.grid(row = 5, column = 1)
def add_numbers(self):
x = self.entry1.get()
y = self.entry2.get()
if x != "" and y != "":
sumxy = int(x) + int(y)
self.lbl3.config(text = "Sum = {}".format(sumxy))
self.entry3.delete(0, "end")
self.entry3.insert(0, sumxy)
self.text1.delete(1.0, "end")
self.text1.insert(1.0, sumxy)
messagebox.showinfo("Sum of {} and {}".format(x,y),
"Sum of {} and {} = {}".format(x, y, sumxy))
if __name__ == "__main__":
root = tk.Tk()
myapp = App(root)
root.mainloop()

Using validation on text entry box

I am trying to set up validation on text entry boxes. Three of the boxes need to only accept integers and one text as a postcode. I am not sure whether to do this in a function previously defined or when the entry boxes are created. Also how would i make the values from the text entry boxes be accessable in the function QuoteCreation. All my code is below.
from tkinter import *
class quote():
def __init__(self, master):
self.master=master
self.master.title("Quote Screen")
self.master.geometry("2100x1400")
self.master.configure(background = "white")
self.Borras = PhotoImage(file = "Borras.Logo.2.gif") #sets up image
self.Borras.image = self.Borras
self.BorrasLabel = Label(self.master, image = self.Borras, bg = "white")#puts image onto label
self.BorrasLabel.place(anchor=NW)
self.Title = Label(self.master, text = "New Quote", font = ("calibri", 20), bg = "White")
self.Title.place(x=650, y = 10)
self.SubmitButton = PhotoImage(file = "Submit.Button.gif") #sets up image
self.SubmitButton.image = self.SubmitButton
self.SubmitButtonLabel = Button(self.master, image = self.SubmitButton, bg = "white", command= self.QuoteCreation)#puts image onto a button
self.SubmitButtonLabel.place(x=900, y=290)
PostCodeVar = StringVar()
PostCodeEntry = Entry(master,width=50, font=20, textvariable=PostCodeVar)
PostCodeEntry.place(x = 20, y = 150)
PostCodeVar.set("Please enter the Post Code")
PostCodeValue = PostCodeVar.get()
HeightVar = StringVar()
HeightEntry = Entry(master, width=50, font=20, textvariable=HeightVar)
HeightEntry.place(x = 20, y = 220)
HeightVar.set("Please enter the Height")
HeightValue = HeightVar.get()
LengthVar = StringVar()
LengthEntry = Entry(master, width=50, font=20, textvariable=LengthVar)
LengthEntry.place(x = 20, y = 290)
LengthVar.set("Please enter the Length")
LengthValue = LengthVar.get()
PitchVar = StringVar()
PitchEntry = Entry(master, width=50, font=20, textvariable=PitchVar)
PitchEntry.place(x = 20, y = 360)
PitchVar.set("Please enter the Pitch")
PitchValue = PitchVar.get()
RiseVar = StringVar()
RiseEntry = Entry(master, width=50, font=20, textvariable=RiseVar)
RiseEntry.place(x = 20, y = 430)
RiseVar.set("Please enter the Rise")
RiseValue = RiseVar.get()
self.SubmitButton = PhotoImage(file = "Submit.Button.gif")
self.SubmitButton.image = self.SubmitButton
self.SubmitButtonLabel = Button(self.master, image = self.SubmitButton, bg = "white", command= self.QuoteCreation)#puts image onto a button
self.SubmitButtonLabel.place(x=900, y=290)
def on_button(self):
print(self.entry.get())
def QuoteCreation(self):
print(' ')
def quitWindow(self):
self.master.destroy()
def backToWelcome(self):
self.master.destroy()
You would set up separate functions to deal with the validation, when the submit button is pressed.
So, as an example, your submit button may look a bit like this:
submitButton = Button(master, text="Submit", command=validation)
The validation, in your case would then want to carry out these checks:
def validation():
postcode = PostCodeVar.get()
length = LengthVar.get()
pitch = PitchVar.get()
rise = RiseVar.get()
if postcodeCheck(postcode) == True and length.isdigit() == True and pitch.isdigit() == True and rise.isdigit() == True:
#carry out chosen process
In your case, you can try setting the postcode, length, pitch and height variables before calling the function, and setting them as global. The postcode should be created, and if it is okay, the function should then:
return True
...so it matches the outcome of the if statement.
I hope this is what you were looking for, and can adapt the example to your specific problem!

Categories