How can I append the priority of this application to the file as seen in the code below?
#Option boxes for assigning a priority level to the request
priority = StringVar()
Radiobutton(self.f4, text = "High", variable=priority, value="1").grid(row = 4, column = 1, sticky = W)
Radiobutton(self.f4, text = "Low", variable=priority, value="2").grid(row = 6, column = 1, sticky = W)
#Button for "Confirm application"
self.app_con = Button(self.f4, text=" Confirm and finish ", command=self.apphistory)
self.app_con.grid(row=4, column=2
def apphistory(self):
fo = open("Application_History.txt", "a")
fo.writelines(["Priotiry: "+str(priority.get()),"\n"])
fo.close
Have you tried changing
variable=priority
to
variable=self.priority
This way, the Radiobutton knows where to search for the variable (in your program / class) instead of looking for it in the Radiobutton-Class itself.
And as mentioned in the comments, you need to create the variable
self.priority
Your scope is obviously wrong.
Try something like this:
self.app_con = Button(self.f4, text=" Confirm and finish ", command=lambda:self.apphistory(priority))
...
def apphistory(self, priority):
fo = open("Application_History.txt", "a")
fo.writelines(["Priotiry: "+str(priority.get()),"\n"])
fo.close()
Related
As stated in the title, how do I prevent the use of global variables in Tkinter? I have started to learn Tkinter and am running into a problem in almost all of my projects. The problem I run into is that most Widgets need to interact with each other creating a lot of dependencies. Below is the code from one of my projects that has this problem. I included all the code from this project because the problem arises throughout the code.
As an example consider the apply_filter function. In order to receive the input from the user, the Apply Filter button's command function needs access to the entry fields. One way this can be solved is by passing all the entry fields to the command function as arguments. However, I don't like this option because this means that I either have to place all the code into on giant block making the code more difficult to read or I have to pass it through multiple functions which leads to the same result. The option I chose here was to make the inputs global so that I can access them from anywhere within my code. At least the code is easier to read then. Of course this is probably not a good solution. When the GUI scales up this will create a giant mess of dependencies that could easily lead to bugs.
Long story short, I want to know: Is there a better way to deal with these dependencies so that I don't need to create tons of global variables or create unreadable code? Thanks in advance for any effort taken.
import tkinter as tk
from tkinter import filedialog as fd
import pandas as pd
import numpy as np
import sys
start_filter_row = 0
start_filter_column = 0
data_location = ""
df = None
root = None
filterr = {"customercode":{"column name": "Customer (code)", "value": None, "row_number": start_filter_row},
"partnr":{"column name": "Part nr", "value": None, "row_number": start_filter_row + 1},
"partclass":{"column name": "Part class", "value": None, "row_number": start_filter_row + 2},
"partfamily":{"column name": "Part familiy", "value": None, "row_number": start_filter_row + 3},
"startdate":{"column name": "Transaction date", "value": None, "row_number": start_filter_row + 4},
"enddate":{"column name": "Transaction date", "value": None, "row_number": start_filter_row + 5}
}
inputs = dict()
'''
Load Data
'''
def load_dataset():
global df
root.update()
database_location = fd.askopenfilename()
df = load_data(database_location)
def load_data(database_location):
try:
df = pd.read_pickle(database_location)
return df
except Exception as e:
print("Data location not set!", e)
root.destroy()
sys.exit()
'''
create Widgets
'''
def create_filter_inputs():
for key, value in filterr.items():
label = tk.Label(root, text = key)
label.grid(row = value["row_number"], column = start_filter_column, sticky = "w")
entry = tk.Entry(root)
entry.focus_set()
entry.grid(row = value["row_number"], column = start_filter_column + 1, sticky = "w")
inputs[key] = entry
def create_filter_buttons():
apply_button = tk.Button(root, text = "Apply Filter", command = apply_filter)
apply_button.grid(row = start_filter_row + len(filterr), column = 0, sticky = "news")
clear_button = tk.Button(root, text = "Clear Filter", command = clear_filter)
clear_button.grid(row = start_filter_row + len(filterr), column = 1, sticky = "news")
'''
Button functions
'''
def clear_filter():
for key, entry in inputs.items():
entry.delete(0, 'end')
apply_filter()
def apply_filter():
for key, entry in inputs.items():
filterr[key]["value"] = entry.get().split(";")
def main():
global data_location, root
'''
Setting up Tkinter Frame and loading dataset
'''
root = tk.Tk()
root.title("Data Tool V0.1")
loading_label = tk.Label(root, text = "Loading...", width = 30, height = 5)
loading_label.pack()
load_dataset()
loading_label.destroy()
n_rows, n_cols = (4, 3)
root.columnconfigure(tuple(range(n_cols)), weight=1)
root.rowconfigure(tuple(range(n_rows)), weight=1)
'''
Setting up GUI
'''
create_filter_inputs()
create_filter_buttons()
root.mainloop()
if __name__ == "__main__":
main()
You can avoid using globals by converting your GUI into a class format and making them class variables instead.
class GUI:
def __init__():
.....
self.myvariable = ...
def function():
newvar = self.myvariable - 5
Its also worth noting that variables defined outside of functions can be seen from within functions, but not vice versa:
>>> def f(a):
... print(a)
...
>>> b=2
>>> f(b)
2
I'm creating a counter to count how many empty cells there are when a user uploads a CSV file. I am also using treeview to display the contents of the CSV. The print("There are", emptyCells.sum(), "empty cells") works and prints the number to the console but I want to display this in a label so the user can view this in the GUI. It is not displaying anything but a "row" is being added to the application after a file has been uploaded where the label should be as everything moves down but no contents are being inserted into the label.
emptyCells = (df[df.columns] == " ").sum()
# print("There are", emptyCells.sum(), "empty cells")
tree.pack(side=BOTTOM, pady=50)
messagebox.showinfo("Success", "File Uploaded Successfully")
stringVariable = StringVar()
printVariable = ("There are", emptyCells.sum(), "empty cells")
#print(printVariable)
stringVariable.set(printVariable)
lbl = Label(windowFrame, textvariable=stringVariable, font=25)
lbl.pack()
According to your question you want to update your tkinter label by a button click. You would do this with something like this:
from tkinter import *
from tkinter import messagebox
root = Tk(className="button_click_label")
root.geometry("200x200")
messagebox.showinfo("Success","Test")
emptyCells = (df[df.columns] == " ").sum()
l1 = Label(root, text="Emptycells?")
def clickevent():
txt = "there are", emptyCells
l1.config(text=txt)
b1 = Button(root, text="clickhere", command=clickevent).pack()
l1.pack()
root.mainloop()
It is not tested with the pandas library but should work for you!
The problem with the tkinter label is not happening when I try to reproduce the problem, the label shows. The cause must be somewhere else in the code.
I've not got pandas installed so I've summed a list instead. This shows a GUI with two labels when I run it.
import tkinter as tk
emptyCells = [ 1, 1, 1, 1, 1, 1, 1 ] # keep it simple.
windowFrame = tk.Tk()
old = tk.StringVar()
stringVariable = tk.StringVar()
old_print = ("There are", sum(emptyCells), "empty cells") # Returns a tuple
printVariable = "There are {} empty cells".format( sum(emptyCells) ) # Returns a string.
old.set( old_print )
stringVariable.set(printVariable)
lbl_old = tk.Label( windowFrame, textvariable = old )
lbl_old.pack()
lbl = tk.Label(windowFrame, textvariable=stringVariable, font=25)
lbl.pack()
windowFrame.mainloop()
Does this work when you run it? Does it help identify where the problem is in the code which doesn't show the labels?
Don't you have the sum you need already in the emptyCells variable? Why do you need to use the .sum() function again in the print statement?
printVariable = f"There are {emptyCells} empty cells"
I have been trying to use tkinter CheckButton widget to edit items in a list - each item of the list is a new checkbutton. I want a save method to save the data to a text file, and a load method to load the info from the text file and mark the checkbutton boxes depending on the items in the list.
Here is my code so far, but the list doesn't seem to change when I check the buttons and update the list/file
Here is my code, I need to know why the list isn't updating when I check the boxes:
import tkinter.messagebox as box
modulesMain = Tk()
moduleChecks = []
def SaveChanges():
# Clear the text file
modules = open("modules.txt", "w") #Write mode to overwrite the whole file
modules.write("") # Put in blank text
modules.close()
modules = open("modules.txt", "a") # Append mode to append the file
for item in moduleChecks:
modules.write(item + "\n")
print(moduleChecks)
appButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[0]).grid()
searchButton = Checkbutton(modulesMain, text = "Test", variable = moduleChecks[1]).grid()
Save = Button(modulesMain, text = "Save Changes", command = SaveChanges).grid()
The variable for each of your checkboxes needs to be an IntVar. Your moduleCheck list is currently un-initialised so when you try to access the elements inside it, you'll get an error.
In the below code (modified from yours), I've initialised the moduleCheck to contain two IntVars.
When you press the save button, it will print to the console the current state of the check boxes.
import tkinter as tk
modulesMain = tk.Tk()
moduleChecks = [tk.IntVar() for i in range(2)]
def SaveChanges():
for idx,item in enumerate(moduleChecks):
print(f"{idx} = {item.get()}")
appCheck = tk.Checkbutton(modulesMain, text = "App", variable = moduleChecks[0])
appCheck.grid()
searchCheck = tk.Checkbutton(modulesMain, text = "Check", variable = moduleChecks[1])
searchCheck.grid()
saveButton = tk.Button(modulesMain, text = "Save Changes", command = SaveChanges)
saveButton.grid()
modulesMain.mainloop()
Here is a basic solution.
import tkinter as tk
root = tk.Tk()
#list of options. doubles as checkbox label text
#appending to this list is all that is necessary to create more options
opts = ['App', 'Search']
#create an StringVar for every option
#this way there doesn't have to be type conversions for reading/writing files
vars = [tk.StringVar(value='0') for _ in opts]
#create checkboxes for every option
for opt, var in zip(opts, vars):
tk.Checkbutton(root, text=opt, variable=var, onvalue='1', offvalue='0').grid(sticky='w')
#write the checkbox values to file
def save_options_state():
with open("modules.txt", "w") as file:
file.write(''.join([v.get() for v in vars]))
#set checkbox values from file
def load_options_state():
with open("modules.txt", "r") as file:
for n, var in zip(list(file.read().strip()), vars):
var.set(n)
#save/load buttons
tk.Button(root, text="save", command=save_options_state).grid()
tk.Button(root, text="load", command=load_options_state).grid(row=2, column=1)
root.mainloop()
Here's a runnable example which I think shows how to do everything you've asked about. It assumes the modules.txt file contains data in the following format, where each line consists of a module's name and whether it's checked or not.
Mod1,0
Mod2,0
Mod3,0
The data in the file is used to create a list of Checkbuttons — one for each module — and initially sets to its current status as indicated in the file.
Here's the sample code:
from tkinter import *
import tkinter.messagebox as box
dataFilename = "modules.txt"
moduleChecks = []
def ReadModules():
with open(dataFilename, "r") as file:
for line in (line.strip() for line in file):
moduleName, moduleState = line.split(',')
moduleChecks.append((moduleName, int(moduleState)))
print('read:', moduleChecks)
def SaveChanges():
print('writing:', moduleChecks)
with open(dataFilename, "w") as file:
for checkButton in moduleCheckbuttons:
moduleName = checkButton.cget('text')
moduleState = checkButton.var.get()
file.write(','.join((moduleName, str(moduleState))) + '\n')
ReadModules()
modulesMain = Tk()
moduleCheckbuttons = []
# Create a Checkbutton for each module based on moduleChecks values.
for moduleName, moduleState in moduleChecks:
intVar = IntVar(value=moduleState)
checkButton = Checkbutton(modulesMain, text=moduleName, variable=intVar)
checkButton.var = intVar # Attach reference to variable.
checkButton.grid()
moduleCheckbuttons.append(checkButton)
saveButton = Button(modulesMain, text="Save Changes", command=SaveChanges)
saveButton.grid()
modulesMain.mainloop()
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am creating a GUI that is meant to emulate an online shop of some sort.
One part of the task is to have a button that will generate a HTML document with images of the user's chosen product category.
Below I have provided my four radio buttons along with their IntVar and commands.
Each of the RadioButton commands do the same thing but extract information from different websites, so for brevity I have only provided the command for the slipper category.
home_hobbies = Tk()
status = IntVar()
def show_slippers():
#open downloaded file to extract info
slipperfile = open('slippers.html','r',encoding = 'utf-8').read()
prices = findall("<span.*value'>(.*)</span>", slipperfile) #regex prices
titles = findall('<h3.*body ">\n\s*(.*)', slipperfile) #regex titles
select_categ.config(state=NORMAL) #make the text box edit-able
select_categ.delete(1.0, END) #delete any text already in the text box
#for loop to find first five items and print them
for i in range(5):
title = titles[i]
price = prices[i]
result = str(i+1) + ". " + title + ' - $' + price + "\n"
select_categ.insert(END, result) #write list of products
select_categ.config(state=DISABLED) #make sure the user can't edit the text box
slippers = Radiobutton(home_hobbies, command = show_slippers, indicator = 'off', variable = status, value = 1, text = 'Winter Slippers')
diy = Radiobutton(home_hobbies, command = show_diy, indicator = 'off', variable = status, value = 2, text = "DIY Supplies")
#newstock radiobuttons
sports = Radiobutton(home_hobbies, command = show_sports, indicator = 'off', variable = status, value = 3, text = "Pool Toys")
novelties = Radiobutton(home_hobbies, command = show_novelties, indicator = 'off', variable = status, value = 4, text = "Novelty Items")
select_categ = Text(home_hobbies, wrap = WORD, font = content_font, bg = widgetbg, fg = fontcolour, width = 40)
Above, I also provided the line of code that generates the Text widget as it may help in answering my question (I don't have a very deep understanding of this widget despite reading the effbot page about 20 times over).
I now have a different button whose task is to generate a HTML doc with it's own command, "show_img":
htmlshow = Button(home_hobbies, text = "View Product Images", command = show_img)
I am trying to make the show_img() command work such that I have a preamble of HTML coding, and then, depending on which radibutton has been chosen, the function will replace sections of the code with the corresponding information:
def show_img():
#in this section I write my HTML code which includes replaceable sections such as "image1" and source_url
if slipper_trig:
table = table.replace("source_url", ' Etsy - Shop Unique Gifts for Everyone')
imgfile = open('slippers.html', 'r', encoding = 'utf-8').read()
images = findall('<img\n*.*image\n*\s*src="(.*)"', imgfile)
for i in range(5):
image = images[i]
table = table.replace("image"+str(i+1), image)
I tried to add BooleanVar into the commands for my Radio Buttons like this:
slipper_trig = False
diy_trig = False
pool_trig = False
novelty_trig = False
#Function for the product category buttons
#
def show_slippers():
#make selected category true and change all others to false
slipper_trig = True
diy_trig = False
pool_trig = False
novelty_trig = False
As a way to distinguish between the categories but the GUI clearly doesn't remember the value of "slipper_trig" after its been defined as true in the "show_slippers" function.
Maybe I need to try and integrate the "show_img" command into my original functions that define the RadioButtons? Maybe I should be figuring out how to determine the category chosen by what's shown in the text box?
Any advice would be appreciated.
Thanks!
You didn't show minimal working code with your problem so I can only show some minimal example with Button and RadioButton to show how to use these widgets.
I don't know if you used command=function_name in Button.
BTW: it has to be function's name without ()
I don't know if you used .get() to get value from StringVar/Intvar/BooleanVar assigned to RadioButtons.
EDIT I added Checkbutton because probably you may need it instead of Radiobutton
import tkinter as tk
# --- functions ---
def on_click():
selected = result_var.get()
print('selected:', selected)
if selected == 'hello':
print("add HELLO to html")
elif selected == 'bye':
print("add BYE to html")
else:
print("???")
print('option1:', option1_var.get()) # 1 or 0 if you use IntVar
print('option2:', option2_var.get()) # 1 or 0 if you use IntVar
if option1_var.get() == 1:
print("add OPTION 1 to html")
if option2_var.get() == 1:
print("add OPTION 2 to html")
# --- main ---
root = tk.Tk()
result_var = tk.StringVar(root, value='hello')
rb1 = tk.Radiobutton(root, text="Hello World", variable=result_var, value='hello')
rb1.pack()
rb2 = tk.Radiobutton(root, text="Good Bye", variable=result_var, value='bye')
rb2.pack()
option1_var = tk.IntVar(root, value=0)
opt1 = tk.Checkbutton(root, text='Option 1', variable=option1_var)
opt1.pack()
option2_var = tk.IntVar(root, value=0)
opt2 = tk.Checkbutton(root, text='Option 2', variable=option2_var)
opt2.pack()
button = tk.Button(root, text='OK', command=on_click)
button.pack()
root.mainloop()
Doc on effbot.org: Button, Radiobutton, Checkbutton
As I working with the Python tkinter GUI and sqlite 3 recently to build a project, I discovered a lots of questions in Python programming. One of the questions is what would be the best way of calling multiple values in a function from another function in python? Meanwhile, I've done some research about retuning and calling value in function, and I know that in python function it can allows to return multiple value, however, when it comes to calling and I want it to call specifically the value I return e.g return (x,y,z), I really not sure how could I call it.
Here are some code that are in my project, please feel free to give me any suggestion about my code and the question that I ask above
First function
def taskUpdateB():
conn = sqlite3.connect("zzzsqlite.db")
booking = conn.cursor()
index = sOLB.curselection()
selTask = sOLB.get(index)
bookinID = selTask[-2]
getBookID = booking.execute('''SELECT bookingID FROM booking
WHERE bookingID=?''', (bookinID,))
taUp = Toplevel()
taUp.title("Task Update")
taskLabel = Label(taUp, text ="Task Update", font=('Times', 20))
taskLabel.grid(row=0)
showLabel = Label(taUp, text ="Please Select and Enter Infomation Below", font=('Times', 18))
showLabel.grid(row=1)
var = IntVar()
var = 0
fullRadio = Radiobutton(taUp, text="Fully", variable=var, value=1, command = taskUpdCom)
fullRadio.grid(row=2, sticky=W)
partRadio = Radiobutton(taUp, text="Partly", variable=var, value=2, command = taskUpdCom)
partRadio.grid(row=3, sticky=W)
notRadio = Radiobutton(taUp, text="Unable", variable=var, value=3, command = taskUpdCom)
notRadio.grid(row=4, sticky=W)
noteLabel = Label(taUp, text ="Note:", font=('Times', 16))
noteLabel.grid(row=5, sticky=W)
noteBox = Text(taUp, width=30, height =20, font=('Arial', 12,), highlightbackground='black')
noteBox.grid(row=6)
comButton = Button(taUp, text ="Task Complete and Send Invoice", command = taskUpdCom)
comButton.grid(row =7)
booking.close()
return (var, noteBox, showLabel, bookinID)
Second function
def taskUpdCom():
a = taskUpdateB
#get the data from the update list
var = taskUpdateB.var()
noteBox = taskUpdateB.noteBox()
showLabel = taskUpdateB.showLabel()
bookinID = taskUpdateB.bookinID()
selProg = str(var.get())
if selProg == 0:
showLabel["text"] = ("***Please Select Task Progress Below***")
elif noteBox.get() == "":
showLabel["text"] = ("***Please Enter Task Note Below***")
else:
conn = sqlite3.connect("zzzsqlite.db")
update = conn.cursor()
wriUpda = zzzsqliteTable.addUpdate(bookinID, selProg, noteBox)
conn.commit()
updata.close()
taskUpdateB.noteBox.delete(0, END)
tkinter.messagebox.showinfo("Notification","Your task has been updated and removed from the list")
try:
deIndex = taskUpdateB.index()#The item selected from task update delete it from the list.... will still in the database.
sOLB.delete(deIndex)
except IndexError:
pass
Please forgive my code has not been fully complete yet, and kind of messy...
Thanks for your help :)
That's not how functions work. You probably want to call the function then unpack the results in the calling scope.
var, noteBox, showLabel, bookinID = taskUpdateB()
While it is true that functions are objects in Python, they aren't stateful processors or something, they just return the result directly and then disappear (well, unless you are doing something fancy, but you aren't).