I am making a program where a user will choose a dataset from a dropdown box, and will subsequently then load the data in for analysis later on in the program. My current problem is that the data is not being loaded in, and I am not sure why
from tkinter import *
datasets = ["dataset 1", "dataset 2", "dataset 3", "dataset 4"]
master = Tk()
variable = StringVar(master)
variable.set(datasets[0])
data_options = OptionMenu(master, variable, *datasets)
data_options.pack()
from sklearn import datasets
import pandas as pd
def dat_import():
if variable.get() == "dataset 1":
data = pd.DataFrame(datasets.load_iris().data)
return data
if variable.get() == "dataset 2":
data = pd.DataFrame(datasets.load_diabetes().data)
return data
if variable.get() == "dataset 3":
data = pd.DataFrame(datasets.load_digits().data)
return data
if variable.get() == "dataset 4":
data = pd.DataFrame(datasets.load_boston().data)
return data
button = Button(master, text = "Import Dataset", command = dat_import)
button.pack()
mainloop()
Simply, all I need is a user to select choice from dropdown box, press import dataset and that dataset is saved as a dataframe for access later in the program.
Any help would be greatly appreciated, because I cannot find anywhere that will explain how to do this!
You should not return anything from a button callback, as it is not accessible. Instead globalize it so you can use it in a later function or anywhere else:
def dat_import():
global data
value = variable.get()
if value == datasets[0]:
data = pd.DataFrame(datasets.load_iris().data)
elif value == datasets[1]:
data = pd.DataFrame(datasets.load_diabetes().data)
elif value == datasets[2]:
data = pd.DataFrame(datasets.load_digits().data)
else:
data = pd.DataFrame(datasets.load_boston().data)
A better way to reduce the code might be to use dictionary dispatching here, so something like:
datasets = ["dataset 1", "dataset 2", "dataset 3", "dataset 4"]
dis = {datasets[0] : pd.DataFrame(datasets.load_iris().data,
datasets[1] : pd.DataFrame(datasets.load_diabetes().data,
datasets[2] : pd.DataFrame(datasets.load_digits().data,
datasets[3] : pd.DataFrame(datasets.load_boston().data,
}
def dat_import():
global data
data = dis[variable.get()]
Related
I'm trying to create a small interface with Tkinter with 4 radio buttons and each time I click on one of the buttons it will print the value of the button, I've managed to do that!
However, I would like the printed value to be saved in an Excel file for example each time I click on one of the buttons in my tkinter window.
Below is the code I used and the result I got:
from tkinter import *
import tkinter as tk
master = Tk()
master.geometry("175x175")
# Tkinter string variable
# able to store any string value
v = IntVar(master, "0") #0 pour qu'aucun des boutons soit sélectioné par défaut
# Dictionary to create multiple buttons
values = {"classe 1" : "1",
"classe 2" : "2",
"classe 3" : "3",
"classe 4" : "4"}
def affiche ():
val = v.get()
print(val)
data = [
['class1', val],
]
labelValue = tk.Label(master,
text="""Choose your class affectation:""",
justify = tk.LEFT,
padx = 20).pack()
for (text, value) in values.items():
Radiobutton(master, text = text, variable = v, command = affiche,
value = value).pack(side =TOP, ipady = 5)
# Infinite loop can be terminated by
# keyboard or mouse interrupt
# or by any predefined function (destroy())
mainloop()
On the right of the capture below are the values I would like to save in an excel file each time I click.
I got them by clicking respectively on class1, class2, class3 and class4
enter image description here
I am a beginner, any help is appreciated!
Thank you very much.
I just completed the previous program
def affiche ():
val = v.get()
print(val)
workbook = xlsxwriter.Workbook('Mon fichier.xlsx')
worksheet = workbook.add_worksheet("My sheet")
data = [
['class1', val],
['class2', val],
['class3', val],
['class4', val],
]
caption = 'My data.'
# Set the columns widths.
worksheet.set_column('B:G', 12)
# Write the caption.
worksheet.write('B1', caption)
# Add a table to the worksheet.
worksheet.add_table('B4:L4', {'header_row': 0})
# Table data can also be written separately, as an array or individual cells.
worksheet.write_row('B4', data[0])
worksheet.write_row('B5', data[1])
worksheet.write_row('B6', data[2])
worksheet.write_row('B7', data[3])
workbook.close()
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 have the following development that I am working on with the Tkinter, ElementTree and Pandas modules in Python:
from tkinter import *
import xml.etree.ElementTree as ET
import pandas as pd
file_xml = ET.parse('example1.xml')
rootXML = file_xml.getroot()
root = Tk()
root.title("Graphical Analysis of Signals of a XML")
root.geometry("1024x820")
root.pack_propagate(False)
root.config(bd=15)
# Functions to open xml file
def open_file():
try:
global temxml, xml_Name
xml_Name = str(easygui.fileopenbox(title='Select xml file', default='*.xml'))
if str(os.path.abspath(xml_Name)) != os.path.join(os.path.abspath(os.getcwd()), os.path.basename(xml_Name)):
menssage.set("Opened xml file: ", xml_Name)
child_4_separate(os.path.basename(str(tempxml)))
else:
child_4_separate(os.path.basename(str(xml_Name)))
except FileNotFoundError:
print('XML file was not loaded.')
# Function to display buttons and choose the Child_4 to be plotted
def child_4_separate(xml_Name):
print("File: ", xml_Name)
file_xml = ET.parse(xml_Name)
data_xml = [
{
"Name": signal.attrib["Name"],
"Id": signal.attrib["Id"],
} for signal in file_xml.findall(".//Child_4")
]
# print(data_xml)
for i in data_xml:
print(i)
id_tc = i.get('Id')
dict_tc = str(i.values()).replace('dict_values([\'', '')
name_tc = dict_tc.replace('\'])', '')
Button(root, text=f"TC> {name_tc}", command=transfor_data_atri_child_4(xml_Name, id_tc)).pack()
# Function to transform xml file to DataFrame
def transfor_data_atri_child_4(rootXML, id_tc):
print("File: ", rootXML)
print("id_tc: ", id_tc)
What I'm trying to do is that every time I click a button, the child_4_separate (xml_Name) function it goes to the transform_data_atri_child_4 (rootXML, id_tc) function with a single id number and does not fetch me everything like it does in the last print that I show below, this is to be able to manipulate them separately.
I share the XML file in this link example1.xml because of how long it is.
I don't know if I need another for inside or what I need, because when trying another for inside the already existing for in the child_4_separate (xml_Name) function it is repeating it many times and it is not what I want, but simply that redirect to the following function with the two parameters that I am indicating, but separately; help me please! Beforehand, thank you very much!
Just for possible searches or related problems later, I share the solution:
Take the value command = lambda x = xml_Name, y = id_tc: transform_data_atri_child_4 (x, y) in the button attributes and it worked, my function is like this:
# Function to display buttons and choose the Child_4 to be plotted
def child_4_separate(xml_Name):
# print("File: ", xml_Name)
file_xml = ET.parse(xml_Name)
data_xml = [
{
"Name": signal.attrib["Name"],
"Id": signal.attrib["Id"],
} for signal in file_xml.findall(".//Child_4")
]
# print(data_xml)
for i in data_xml:
id_tc = i.get('Id')
dict_tc = str(i.values()).replace('dict_values([\'', '')
name_tc = dict_tc.replace('\'])', '')
Button(root, text=f"TC> {name_tc}", command=lambda x=xml_Name, y=id_tc: transfor_data_atri_child_4(x, y)).pack()
I appreciate the solution to #Sujay. Happy codification everyone!!
While working on my project I came across a problem regarding tabs and tabgroup in PySimpleGUI.
Is there a function that returns currently selected tab?
Having many tabs I would like being able to select each of them (but not at the same time) and return the key of the currently selected one/active one.
I've tried .Get() and .Select() but seems like these don't do it or maybe I'm doing something wrong? Please keep in mind I'm a total beginner.
I also made sure to enable events in every tab.
I did something like this: curr_sel_tab= tab_group.find_key_from_tab_name(tab_group.Get()) but it returns the name of the tab instead of the key.
Basically my app is connected to a database. What I'm trying to do is to select a row in a tab(1,2,3 or 4) and delete it. But in order to do it I need a key of the tab(I guess).
Below you can find a fragment of code I stuck upon as well as a screenshot of my app.
import PySimpleGUI as sg
import baza # external file with my databse
sg.theme("GreenTan")
left_col = [sg.Button("Create")],[sg.Button("Read")],[sg.Button("Update")],[sg.Button("Delete")]
data = baza.get_db_obiad()
print(data)
headings2 = ['Id', 'Name', '1', '2', '3']
layout_1 = [[sg.Table(values=data[0:][:], headings=headings2, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_1-',
row_height=35)]]
data1 = baza.get_db_podkladka()
headings3 = ['Id','Name']
layout_2 = [[sg.Table(values=data1[0:][:], headings=headings3, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_2-',
row_height=35)]]
data2 = baza.get_db_mieso()
headings4 = ['Id','Name']
layout_3 = [[sg.Table(values=data2[0:][:], headings=headings4, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_3-',
row_height=35)]]
data3 = baza.get_db_dodatki()
headings5 = ['Id','Name']
layout_4 = [[sg.Table(values=data3[0:][:], headings=headings5, max_col_width= True,
auto_size_columns=False,
display_row_numbers=False,
enable_events=True,
justification='c',
alternating_row_color='lightyellow',
key='-TAB_4-',
row_height=35)]]
tab_group = sg.TabGroup([[sg.Tab("Tab 1", layout_1),
sg.Tab("Tab 2", layout_2),
sg.Tab("Tab 3", layout_3),
sg.Tab("Tab 4", layout_4)]],
enable_events=True)
right_col = [[tab_group]]
layout = [[sg.Column(left_col, justification="c"), sg.Column(right_col)]]
window = sg.Window("", layout).Finalize()
window.Maximize()
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == "Exit":
break
elif event == "Create":
pass
elif event == "Read":
pass
elif event == "Update":
pass
elif event == "Delete":
if sg.popup_yes_no("Are you sure you want to delete this record?"):
curr_sel_tab= tab_group.find_key_from_tab_name(tab_group.Get())
print(curr_sel_tab)
else:
break
window.close()
Screenshot from my app
I had a similar issue! I was able to figure out that the values object contains the key of the tab that has been clicked on. You would just need to figure out what index its at. Before you do that, it would be a good idea to give each tab a key so that you know what tab was clicked on. Made some edits to your code below. Notice how I added the keys to teach tab:
tab_group = sg.TabGroup([[sg.Tab("Tab 1", layout_1, key= "tab1"),
sg.Tab("Tab 2", layout_2, key="tab2"),
sg.Tab("Tab 3", layout_3, key="tab3"),
sg.Tab("Tab 4", layout_4, key="tab4")]],
enable_events=True)
If you print out the values object and "Tab 1" is clicked, then you will see its key, "tab1" in this case, inside the values object. Same applies for the other tabs if they are clicked.
Hope this helps!
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"