I have a function that will .destroy()
a custom scrollbar and then recreates the scrollbar with a new theme. My IDE (Eclipse) is telling me that
my function contains Undefined Variables
. The error is not stopping me from running my program and I know that if the variable is not there my try
statement will run a different code to create the scrollbar variable. I also know I can use #@UndefinedVariable
to tell my IDE to not worry about the undefined variable.
Keep 2 things in mind:
My scrollbar is custom. It is not the tkinter scrollbar. I have this custom scrollbar so I can change the colors(theme) of the sliders, background, and arrows on the scrollbar as the tkinter scrollbar cannot do this on Windows or Mac machines.
My custom scrollbar does not currently have a way to manipulate the colors once it has been initialized. Because of this I decided the best way to change the theme of my scrollbar was to create a
try
statement that would first try to destroy the scrollbars and recreate them with the new theme or onexcept
create the scrollbars because there was none to begin with.
My question is this:
Is it a problem for me to manage my scrollbar this way? Should I be going about this a different way?
I just feel like I am using the try
statement in a way it was not meant to be used. Maybe I am just over thinking this and it is fine but it's best to know for sure so I don't make a habit of doing things the wrong way.
Below is the chopped down version of how I create and manage my scrollbars:
from tkinter import *
import scrollBarClass #Custom scrollbar class
pyBgColor = "#%02x%02x%02x" % (0, 34, 64)
pyFrameColor = "#%02x%02x%02x" % (0, 23, 45)
root = Tk()
root.title("MINT: Mobile Information & Note-taking Tool")
root.geometry("500x500")
root.config(bg = pyFrameColor)
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
currentTextColor = 'orange'
def doNothing():
print("Do lots of nothing?")
# ~~~~~~~~~~~~~~~~~< Theme >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def MintThemeDefault(mainBG, textBG, txtColor):
# Some theme configs
# More theme configs
# and so on...
root.text.config(bg = textBG, fg = txtColor)
try:
vScrollBar.destroy() #@UndefinedVariable
hScrollBar.destroy() #@UndefinedVariable
makeScrollBars(textBG, txtColor, mainBG)
except:
makeScrollBars(textBG, txtColor, mainBG)
def makeScrollBars(textBG,txtColor,mainBG):
vScrollBar = scrollBarClass.MyScrollbar(root, width=15, command=root.text.yview, troughcolor = textBG,
buttontype = 'square', thumbcolor = txtColor, buttoncolor = mainBG)
vScrollBar.grid(row = 0, column = 1, columnspan = 1, rowspan = 1, padx =0, pady =0, sticky = N+S+E)
root.text.configure(yscrollcommand=vScrollBar.set)
vScrollBar.config(background = mainBG)
hScrollBar = scrollBarClass.MyScrollbar(root, height=15, command=root.text.xview, orient='horizontal', troughcolor = textBG,
buttontype = 'square', thumbcolor = txtColor, buttoncolor = mainBG)
hScrollBar.grid(row = 1 , column = 0, columnspan = 1, rowspan = 1, padx =0, pady =0, sticky = S+W+E)
root.text.configure(xscrollcommand=hScrollBar.set)
hScrollBar.config(background = mainBG)
# ~~~~~~~~~~~~~~~~~< THEMES >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def MintTheme1():
mainBGcolor = "#%02x%02x%02x" % (64,89,82)
textBGcolor = "#%02x%02x%02x" % (17,41,41)
txtColor = "#%02x%02x%02x" % (175, 167, 157)
MintThemeDefault(mainBGcolor,textBGcolor,txtColor)
def MintTheme2():
global currentTextColor
mainBGcolor = "#%02x%02x%02x" % (14, 51, 51)
textBGcolor = "#%02x%02x%02x" % (4, 22, 22)
txtColor = "#%02x%02x%02x" % (223, 171, 111)
MintThemeDefault(mainBGcolor,textBGcolor,txtColor)
# ~~~~~~~~~~~~~~~~~< Theme Menu >~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def libraryMenu():
menu = Menu(root)
root.config(menu=menu)
prefMenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Preferences", menu=prefMenu)
prefMenu.add_command(label = "Mint Theme 1", command = MintTheme1)
prefMenu.add_command(label = "Mint Theme 2", command = MintTheme2)
libraryMenu()
# ~~~~~~~~~~~~~~~~~< FRAMES >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
root.text = Text(root, undo = True)
root.text.grid(row = 0, column = 0, rowspan = 1, columnspan = 1, padx =0, pady =0, sticky = N+S+E+W)
root.text.config(bg = pyFrameColor, fg = "white", font=('times', 16), insertbackground = "orange")
root.text.config(wrap=NONE)
# ~~~~~~~~~~~~~~~~~< Default Theme >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MintThemeDefault("#%02x%02x%02x"%(64,89,82),"#%02x%02x%02x"%(0, 23, 45),"#%02x%02x%02x"%(175, 167, 157))
root.mainloop()
In order for you to test this code you will need the scrollBarClass.py file. Here is my Github Link for the file. Just put the scrollBarClass.py file in the same directory as the main.py file you are using to test the code with.
I am adding the complete code for review. Note that this program works fine without any major errors but does require a few files to function. See my GitHub for this project called MINT.
from tkinter import *
import time
import tkinter.messagebox
import tkinter.simpledialog
import json
from string import ascii_letters, digits
import os
import scrollBarClass
# Created on Mar 21, 2017
# @author: Michael A McDonnal
pyBgColor = "#%02x%02x%02x" % (0, 34, 64)
pyFrameColor = "#%02x%02x%02x" % (0, 23, 45)
root = Tk()
root.title("MINT: Mobile Information & Note-taking Tool")
root.geometry("1050x900")
root.minsize(800,600)
root.config(bg = pyFrameColor)
root.columnconfigure(0, weight=0)
root.columnconfigure(1, weight=1)
root.rowconfigure(0, weight=0)
root.rowconfigure(1, weight=1)
#root.rowconfigure(2, weight=1)
#~~~~~~~~~~~~~~~~~~~< Windows stuff >~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# row0label = Label(root)
# row0label.grid(row = 0 , column = 0 )
# row0label.configure(text = " ")
#~~~~~~~~~~~~~~~~~~~< Global Variables Being Uses >~~~~~~~~~~~~~~~~~~~~~~~~~~
path = "./NotesKeys/"
colorPath = "./Colors/"
notebook = dict()
currentWorkingLib = ""
currentWorkingKeys = ""
currentWorkingButtonColor = "orange"
selectedTextColor = "orange"
selectedBGColor = "#%02x%02x%02x"
postUpdate = False
#~~~~~~~~~~~~~~~~~~~< USE TO open all files in Directory >~~~~~~~~~~~~~~~~~~~
with open("%s%s"%(path,"list_of_all_filenames"), "r") as listall:
list_of_all_filenames = json.load(listall)
def openAllFiles():
global path
for filename in os.listdir(path):
with open(path+filename, "r") as f:
notebook[filename] = json.load(f)
openAllFiles()
#~~~~~~~~~~~~~~~~~~~< Prompt For New Library >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
valid_filename = ""
def new_lib_prompt():
global valid_filename, list_of_all_filenames, path
a_name = tkinter.simpledialog.askstring("Create New Note Library", "Alphanumeric and '_' only", initialvalue = "Name_Here")
VALID_CHARS = "-_.() {}{}".format(ascii_letters, digits)
valid_filename = ("".join(c for c in a_name if c in VALID_CHARS)).replace(" ", "_").lower()
if valid_filename != "" and valid_filename != "name_here":
if valid_filename not in list_of_all_filenames:
createNewNotesAndKeys(valid_filename)
list_of_all_filenames.append(valid_filename)
with open("%s%s"%(path,"list_of_all_filenames"), "r+" ) as f:
json.dump(list_of_all_filenames, f, indent = "")
libraryMenu()
else:
print ("Library already exist")
else:
print ("No Name Given")
def createNewNotesAndKeys(name):
global path, list_of_all_filenames
nName = name+"_notes"
kName = name+"_keys"
with open("./NotesKeys/default_notes", "r") as defaultN:
nBase = json.load(defaultN)
with open("./NotesKeys/default_keys", "r") as defaultK:
kBase = json.load(defaultK)
with open("%s%s"%(path,nName), "w") as outNotes:
json.dump(nBase, outNotes, indent = "")
with open("%s%s"%(path,kName), "w") as outNotes:
json.dump(kBase, outNotes, indent = "")
openAllFiles()
#~~~~~~~~~~~~~~~~~~~< USE TO CLOSE PROGRAM >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def closeprogram():
answer = tkinter.messagebox.askquestion("Leaving MINT?","Are you sure you want to leave MINT")
if answer == "yes":
root.destroy()
else:
tkinter.messagebox.showinfo("MINTy Fresh!","Welcome Back XD")
def doNothing():
print("Do lots of nothing?")
#~~~~~~~~~~~~~~~~~~~< Message Box >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def ihnb():
answer = tkinter.messagebox.askquestion("Do you want to be a Python Programmer?","Do you want to program?")
if answer == "yes":
a1 = "Then be prepared to spend countless hours hating life!"
root.text.delete(1.0, "end-1c")
root.text.insert("end-1c", a1)
root.text.see("end-1c")
else:
a2= "Smart move. Now go away!"
root.text.delete(1.0, "end-1c")
root.text.insert("end-1c", a2)
root.text.see("end-1c")
#~~~~~~~~~~~~~~~~~~~< UPDATE keyword display >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def update_kw_display():
pass
listToPass = ["chose a library","chose a library_keys","chose a library_notes",""]
if currentWorkingKeys not in listToPass:
keys_to_be_updated = notebook[currentWorkingKeys]
root.textSideL.delete(1.0, "end-1c")
root.textSideR.delete(1.0, "end-1c")
contr = 0
for item in keys_to_be_updated:
if contr == 0:
root.textSideL.insert("end-1c",item+"\n")
root.textSideL.see("end-1c")
contr += 1
else:
root.textSideR.insert("end-1c",item+"\n")
root.textSideR.see("end-1c")
contr = 0
else:
print("In the list to pass")
#~~~~~~~~~~~~~~~~~~~< Search for words and highlight >~~~~~~~~~~~~~~~~~~~~~~~~
def searchTextbox(event=None):
root.text.tag_configure("search", background="green")
root.text.tag_remove('found', '1.0', "end-1c")
wordToSearch = searchEntry.get().lower()
idx = '1.0'
while idx:
idx = root.text.search(wordToSearch, idx, nocase=1, stopindex="end-1c")
if idx:
lastidx = '%s+%dc' % (idx, len(wordToSearch))
root.text.tag_add('found', idx, lastidx)
idx = lastidx
root.text.tag_config('found', font=("times", 16, "bold"), foreground ='orange')
#~~~~~~~~~~~~~~~~~~~< UPDATE selected_notes! >~~~~~~~~~~~~~~~~~~~
def append_notes():
global currentWorkingLib, currentWorkingKeys, path
e1Current = keywordEntry.get().lower()
e1allcase = keywordEntry.get()
e2Current = root.text.get(1.0, "end-1c")
answer = tkinter.messagebox.askquestion("Update Notes!","Are you sure you want update your Notes for "+e1allcase+" This cannot be undone!")
if answer == "yes":
if e1Current in notebook[currentWorkingLib]:
statusE.config(text = "Updating Keyword & Notes for the "+currentWorkingLib+" Library!")
dict_to_be_updated = notebook[currentWorkingLib]
dict_to_be_updated[e1Current] = e2Current
with open("%s%s"%(path,currentWorkingLib),"w") as working_temp_var:
json.dump(dict_to_be_updated, working_temp_var, indent = "")
statusE.config(text = "Update Complete")
else:
statusE.config(text= "Creating New Keyword & Notes for the "+currentWorkingLib+" Library!")
dict_to_be_updated = notebook[currentWorkingLib]
dict_to_be_updated[e1Current] = e2Current
with open("%s%s"%(path,currentWorkingLib), "w" ) as working_temp_var:
json.dump(dict_to_be_updated, working_temp_var, indent = "")
keys_to_be_updated = notebook[currentWorkingKeys]
keys_to_be_updated.append(e1allcase)
with open("%s%s"%(path,currentWorkingKeys), "w" ) as working_temp_keys:
json.dump(keys_to_be_updated, working_temp_keys, indent = "")
statusE.config(text = "Update Complete")
update_kw_display()
else:
tkinter.messagebox.showinfo("...","That was close!")
#~~~~~~~~~~~~~~~~~~~< Entry Widget >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def kw_entry(event=None):
global currentWorkingLib
e1Current = keywordEntry.get().lower()
#e1IgnoreCase = keywordEntry.get()
if currentWorkingLib in notebook:
note_var = notebook[currentWorkingLib]
if e1Current in note_var:
#tags_list=[r"(?:<<)",r"(?:>>)",r"(?:<)",r"(?:>)"]
root.text.delete(1.0, "end-1c")
root.text.insert("end-1c", note_var[e1Current])
root.text.see("end-1c")
else:
root.text.delete(1.0, "end-1c")
root.text.insert("end-1c", "Not a Keyword")
root.text.see("end-1c")
else:
root.text.delete(1.0, "end-1c")
root.text.insert("end-1c", "No Library Selected")
root.text.see("end-1c")
#~~~~~~~~~~~~~~~~~~~< Preset Themes >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
baseBGimage=PhotoImage(file="./Colors/pybgbase.png")
bgLable = Label(root, image= baseBGimage)
bgLable.place(x = 0, y = 0)
bgLable.config(image = baseBGimage)
bgLable.image = baseBGimage
currentTextColor = 'orange'
def MintThemeDefault(mainBG, textBG, txtColor,bgimage):
global currentTextColor
currentTextColor = txtColor
themeBGimage = bgimage
textFrame.config(bg = textBG)
entryBGimage.config(image = themeBGimage)
entryBGimage.image = themeBGimage
kwBGimage.config(image = themeBGimage)
kwBGimage.image = themeBGimage
bgLable.config(image = themeBGimage)
bgLable.image = themeBGimage
#entryBGimage.config(image = themeBGimage)
#entryBGimage.image = themeBGimage
root.config(bg = mainBG)
root.text.config(bg = textBG, fg = txtColor)
root.textSideL.config(bg = textBG, fg = txtColor)
root.textSideR.config(bg = textBG, fg = txtColor)
searchEntry.config(fg = txtColor, bg = textBG)
keywordEntry.config(fg = txtColor, bg = textBG)
statusFrame.config(bg = textBG)
statusE.config(fg = txtColor, bg = textBG)
statusW.config(fg = txtColor, bg = textBG)
searchLabel.config(fg = txtColor, bg = textBG)
keywordLabel.config(fg = txtColor, bg = textBG)
UpdateKeywordsButton.config(fg = txtColor, bg = textBG)
try:
vScrollBar.destroy() #@UndefinedVariable
hScrollBar.destroy() #@UndefinedVariable
makeScrollBars(textBG, txtColor, mainBG)
except:
makeScrollBars(textBG, txtColor, mainBG)
def makeScrollBars(textBG,txtColor,mainBG):
vScrollBar = scrollBarClass.MyScrollbar(textFrame, width=15, command=root.text.yview, troughcolor = textBG,
buttontype = 'square', thumbcolor = txtColor, buttoncolor = mainBG)
vScrollBar.grid(row = 0, column = 2, columnspan = 1, rowspan = 1, padx =0, pady =0, sticky = N+S+E)
root.text.configure(yscrollcommand=vScrollBar.set)
vScrollBar.config(background = mainBG)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hScrollBar = scrollBarClass.MyScrollbar(textFrame, height=15, command=root.text.xview, orient='horizontal', troughcolor = textBG,
buttontype = 'square', thumbcolor = txtColor, buttoncolor = mainBG)
hScrollBar.grid(row = 1 , column = 0, columnspan = 1, rowspan = 1, padx =0, pady =0, sticky = S+W+E)
root.text.configure(xscrollcommand=hScrollBar.set)
hScrollBar.config(background = mainBG)
def MintTheme1():
mainBGcolor = "#%02x%02x%02x" % (64,89,82)
textBGcolor = "#%02x%02x%02x" % (17,41,41)
txtColor = "#%02x%02x%02x" % (175, 167, 157)
bgimage=PhotoImage(file="./Colors/theme1bg.png")
MintThemeDefault(mainBGcolor,textBGcolor,txtColor,bgimage)
def MintTheme2():
global currentTextColor
mainBGcolor = "#%02x%02x%02x" % (14, 51, 51)
textBGcolor = "#%02x%02x%02x" % (4, 22, 22)
txtColor = "#%02x%02x%02x" % (223, 171, 111)
bgimage=PhotoImage(file="./Colors/theme2bg.png")
MintThemeDefault(mainBGcolor,textBGcolor,txtColor,bgimage)
#~~~~~~~~~~~~~~~~~~~< Menu function >~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def updateWorkingLibKeys(filename):
global currentWorkingLib,currentWorkingKeys
currentWorkingLib = "{}_notes".format(filename).lower()
currentWorkingKeys = "{}_keys".format(filename).lower()
update_kw_display()
def libraryMenu():
menu = Menu(root)
root.config(menu=menu)
fileMenu = Menu(menu, tearoff=0)
menu.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Save", command=doNothing)
fileMenu.add_command(label="Save As", command=doNothing)
fileMenu.add_separator()
fileMenu.add_command(label="Exit", command= closeprogram)
libMenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Note Libraries", menu=libMenu)
libMenu.add_command(label="Library Help Page - Not Implemented Yet", command=doNothing)
libMenu.add_separator()
libMenu.add_command(label="New Library", command=new_lib_prompt)
libMenu.add_command(label="Lock Library - Not Implemented Yet", command=doNothing)
libMenu.add_command(label="Delete Library! - Not Implemented Yet", command=doNothing)
libMenu.add_separator()
prefMenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Preferences", menu=prefMenu)
prefMenu.add_command(label="Mint Theme 1", command=MintTheme1)
prefMenu.add_command(label="Mint Theme 2", command=MintTheme2)
helpMenu = Menu(menu, tearoff=0)
menu.add_cascade(label="Help", menu=helpMenu)
helpMenu.add_command(label="Info", command=doNothing)
for filename in list_of_all_filenames:
libMenu.add_command(label = "%s"%(filename), command = lambda filename=filename: updateWorkingLibKeys(filename))
libraryMenu()
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
textFrame = Frame(root, borderwidth = 0, highlightthickness = 0)
textFrame.grid(row = 0, column = 1, columnspan = 1, rowspan = 2, padx =0, pady =0, sticky = W+E+N+S)
textFrame.columnconfigure(0, weight=1)
textFrame.rowconfigure(0, weight=1)
textFrame.columnconfigure(1, weight=0)
textFrame.rowconfigure(1, weight=0)
entryFrame = Frame(root)
entryFrame.grid(row = 0, column = 0, rowspan = 1, columnspan = 1, padx =0, pady =0, sticky = W+E+N+S)
entryFrame.columnconfigure(0, weight=0)
entryFrame.columnconfigure(1, weight=0)
entryFrame.rowconfigure(0, weight=0)
entryFrame.rowconfigure(1, weight=0)
entryFrame.rowconfigure(2, weight=0)
entryBGimage = Label(entryFrame, image= baseBGimage, borderwidth = 0, highlightthickness = 0)
entryBGimage.image = baseBGimage
entryBGimage.place(x = 0, y = 0)
entryBGimage.config(image = baseBGimage)
kwListFrame = Frame(root, borderwidth = 0, highlightthickness = 0)
kwListFrame.grid(row = 1, column = 0, rowspan = 1, columnspan = 1, padx =0, pady =0, sticky = W+E+N+S)
kwListFrame.columnconfigure(0, weight=1)
kwBGimage = Label(kwListFrame, image= baseBGimage, borderwidth = 0, highlightthickness = 0)
kwBGimage.image = baseBGimage
kwBGimage.place(x = 0, y = 0)
kwBGimage.config(image = baseBGimage)
root.textSideL = Text(kwListFrame, width = 10, height = 20)
root.textSideL.place( x = 5, y = 5)
root.textSideL.config(wrap=NONE)
root.textSideR = Text(kwListFrame, width = 10, height = 20)
root.textSideR.place( x = 95, y = 5)
root.textSideR.config(wrap=NONE)
statusFrame = Frame(root)
statusFrame.config(bg = pyFrameColor)
statusFrame.grid(row = 3, column = 0, rowspan = 3, columnspan = 2, padx =0, pady =0, sticky = W+E+N+S)
statusFrame.columnconfigure(0, weight=1)
statusFrame.columnconfigure(1, weight=1)
statusFrame.rowconfigure(0, weight=0)
root.text = Text(textFrame, undo = True)
root.text.grid(row = 0, column = 0, rowspan = 1, columnspan = 1, padx =0, pady =0, sticky = W+E+N+S)
root.text.config(bg = pyFrameColor, fg = "white", font=('times', 16), insertbackground = "orange")
root.text.config(wrap=NONE)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
statusW = Label(statusFrame, font=("times", 16, "bold"), fg = "white", bg = "black", relief = SUNKEN, anchor = W)
statusW.grid(row = 0, column = 0, padx =1, pady =1, sticky = W+S)
statusW.config(text = "Operation Status", bg = "#%02x%02x%02x"%(0, 23, 45))
statusE = Label(statusFrame, font=("times", 16, "bold"), fg = "white", bg = "black", relief = SUNKEN, anchor = E)
statusE.grid(row = 0, column = 1, padx =1, pady =1, sticky = E+S)
statusE.config(bg = "#%02x%02x%02x"%(0, 23, 45))
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
searchLabel = Label(entryFrame)
searchLabel.grid(row = 1, column = 0, padx =5, pady=5)
searchLabel.config(text="Search Text Field")
searchEntry = Entry(entryFrame, width = 20)
searchEntry.bind("<Return>", searchTextbox)
searchEntry.grid(row = 1, column = 1, padx =5, pady=5)
keywordLabel = Label(entryFrame)
keywordLabel.grid(row = 0, column = 0, padx =5, pady=5)
keywordLabel.config(text="Keyword Search")
keywordEntry = Entry(entryFrame, width = 20)
keywordEntry.bind("<Return>", kw_entry)
keywordEntry.grid(row = 0, column = 1, padx =5, pady=5)
UpdateKeywordsButton = tkinter.Button(entryFrame, fg = 'Black', bg = 'Orange', text = "Update Notes", command = append_notes)
UpdateKeywordsButton.grid(row = 2, column = 0, padx =5, pady =5)
MintThemeDefault("#%02x%02x%02x"%(64,89,82),"#%02x%02x%02x"%(0, 23, 45),"#%02x%02x%02x"%(175, 167, 157),PhotoImage(file="./Colors/pybgbase.png"))
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
status_time = ""
def tick():
global status_time
time2 = time.strftime("%H:%M:%S")
if time2 != status_time:
status_time = time2
statusE.config(text=time2+" Preparing to do nothing...")
statusE.after(200, tick)
tick()
#~~~~~~~~~~~~~~~~~~~< root Main Loop >~~~~~~~~~~~~~~~~~~~~~~~~~~~~
root.mainloop()