Here, I have two classes. First one (MedicalDevices.py) has the functions that I wanna use on the second class (tkinter_devices.py).
I am new to python and the help is appreciate it
Issue I am facing:
I am trying to enter information of devices to excel sheet, but every time I entered more than one device's information, it only save the last entries that I entered while the tkinter running.
MedicalDevices.py
import datetime
import pandas as pd
# excel file path
f_path = "medical_devices_data.xlsx"
df = pd.read_excel(f_path, sheet_name="Sheet1")
class MedicalDevices:
# class attributes: to keep truck the number of the objects that been made.
num_of_devices = 0
# initialization/ construction
def __init__(self, name, sn, manufacture, model, supplier, department='In Store', warranty_expire="Expired"):
# object attributes (to make private attribute, add __ before the name
self.name = name
self.sn = sn
self.manufacture = manufacture
self.model = model
self.supplier = supplier
self.department = department
self.warranty_expire = warranty_expire
# increase number of devices made with every initialization
MedicalDevices.num_of_devices += 1
# dunder (double underscore)method
# override function to print string instead of printing the memory address of an object
def __str__(self):
return f"Name: {self.name}\nSerial#: {self.sn}\nManufacture: {self.manufacture}\n" \
f"Model: {self.model}\nSupplier: {self.supplier}\nDepartment: {self.department}\n" \
f"Warranty expire on: {self.warranty_expire}"
def add_new_device(newDevice):
SerA = df["Name"]
SerB = df["Serial"]
SerC = df["Manufacture"]
SerD = df["Model"]
SerE = df["Supplier"]
SerF = df["Department"]
SerG = df["Warranty"]
# this must be Series type to be able to concatenate with DataFrame
A = pd.Series(newDevice.name)
B = pd.Series(newDevice.sn)
C = pd.Series(newDevice.manufacture)
D = pd.Series(newDevice.model)
E = pd.Series(newDevice.supplier)
F = pd.Series(newDevice.department)
G = pd.Series(newDevice.warranty_expire)
SerA = SerA.append(A)
SerB = SerB.append(B)
SerC = SerC.append(C)
SerD = SerD.append(D)
SerE = SerE.append(E)
SerF = SerF.append(F)
SerG = SerG.append(G)
df2 = pd.DataFrame({"Name": SerA,
"Serial": SerB,
"Manufacture": SerC,
"Model": SerD,
"Department": SerE,
"Supplier": SerF,
"Warranty": SerG})
df2.to_excel(f_path, index=False)
print(f"A new device added with Serial#: {newDevice.sn}")
#
# def search_by_sn(self, sn):
# if sn in df.values:
# print("Got it. Here is the device(s) info: \n-------------------------------")
# print(df.loc[df.Serial == sn])
# else:
# print("Not found")
# newInput = input("Do you want to add a new device (yes/ no): ")
# if newInput == "yes":
# self.add_new_device(DC_1)
# else:
# print("Ok")
# To check Warranty
def check_warranty(self):
date = datetime.datetime.strptime(self.warranty_expire, '%Y/%m/%d').date()
if date >= datetime.date.today():
print(self.name + " is still in Warranty\n" + "Warranty expires on " + self.warranty_expire)
else:
print("Attention, Warranty expired on " + self.warranty_expire)
tkinter_devices.py
import tkinter as tk
from tkinter import ttk
import pandas as pd
from MedicalDevices import MedicalDevices
font1 = ("Verdana", 12)
font2 = ("Verdana", 10)
pageSize = "500x300"
f_path = "medical_devices_data.xlsx"
df = pd.read_excel(f_path, sheet_name="Sheet1")
class MedicalDeviceApp(tk.Tk):
# args=argument (any number of argument (unlimited))
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Medical Devices Data")
tk.Tk.wm_geometry(self, pageSize) # (width x height)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, AddDevice, PageTwo):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Start Page", font=font1)
label.grid(row=0, column=5, sticky="nsew")
bot1 = ttk.Button(self, text="Add a new device",
command=lambda: controller.show_frame(AddDevice))
bot1.grid(row=1, column=0, sticky="nsew", columnspan=1, ipadx=5)
bot2 = ttk.Button(self, text="Page Two",
command=lambda: controller.show_frame(PageTwo))
bot2.grid(row=2, column=0, sticky="nsew")
bot_exit = ttk.Button(self, text="Exit", command=lambda: controller.destroy())
bot_exit.grid(row=3, column=0, sticky="nsew")
class AddDevice(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# Labels
label1 = ttk.Label(self, text="Enter a new device information", font=font1)
label1.grid(row=0, column=1)
label2 = ttk.Label(self, text="Required information with *")
label2.grid(row=1, column=1, pady=5)
label_name = ttk.Label(self, text="Device Name:", font=font2)
label_name.grid(row=2, column=0, sticky="w")
label_serial = ttk.Label(self, text="Serial Number:", font=font2)
label_serial.grid(row=3, column=0, sticky="w")
label_manf = ttk.Label(self, text="Manufacture:", font=font2)
label_manf.grid(row=4, column=0, sticky="w")
label_model = ttk.Label(self, text="Model:", font=font2)
label_model.grid(row=5, column=0, padx=2, sticky="w")
label_supplier = ttk.Label(self, text="Supplier:", font=font2)
label_supplier.grid(row=6, column=0, padx=2, sticky="w")
label_depart = ttk.Label(self, text="Department:", font=font2)
label_depart.grid(row=7, column=0, padx=2, sticky="w")
label_expire = ttk.Label(self, text="Expire Date:", font=font2)
label_expire.grid(row=8, column=0, padx=2, sticky="w")
# Entries
self.entry_name = ttk.Entry(self)
self.entry_name.grid(row=2, column=1, ipadx=20, pady=5)
self.entry_serial = ttk.Entry(self)
self.entry_serial.grid(row=3, column=1, ipadx=20, pady=5)
self.entry_manf = ttk.Entry(self)
self.entry_manf.grid(row=4, column=1, ipadx=20, pady=5)
self.entry_model = ttk.Entry(self)
self.entry_model.grid(row=5, column=1, ipadx=20, pady=5)
self.entry_supplier = ttk.Entry(self)
self.entry_supplier.grid(row=6, column=1, ipadx=20, pady=5)
self.entry_depart = ttk.Entry(self)
self.entry_depart.grid(row=7, column=1, ipadx=20, pady=5)
self.entry_expire = ttk.Entry(self)
self.entry_expire.grid(row=8, column=1, ipadx=20, pady=5)
# Bottoms
self.bot_save = ttk.Button(self, text="Save", command=self.save)
self.bot_save.grid(row=2, column=2, ipadx=20, pady=5, columnspan=1)
self.bot_clear = ttk.Button(self, text="Clear", command=self.delete)
self.bot_clear.grid(row=4, column=2, ipadx=20, pady=2, columnspan=1)
self.bot_back = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
self.bot_back.grid(row=6, column=2, ipadx=20)
self.bot_exit = ttk.Button(self, text="Exit", command=lambda: controller.destroy())
self.bot_exit.grid(row=8, column=2, ipadx=25)
def delete(self):
self.entry_name.delete(0, 'end')
self.entry_serial.delete(0, 'end')
self.entry_manf.delete(0, 'end')
self.entry_model.delete(0, 'end')
self.entry_supplier.delete(0, 'end')
self.entry_depart.delete(0, 'end')
self.entry_expire.delete(0, 'end')
def save(self):
if int(self.entry_serial.get()) in df.values:
print("found it ")
self.delete()
else:
device = MedicalDevices(self.entry_name.get(), self.entry_serial.get(), self.entry_manf.get(),
self.entry_model.get(), self.entry_supplier.get(), self.entry_depart.get(),
self.entry_expire.get())
MedicalDevices.add_new_device(device)
self.delete()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Page Tow", font=font1)
label.pack(pady=10, padx=10)
bot1 = ttk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
bot1.pack()
app = MedicalDeviceApp()
app.mainloop()
Inside add_new_device(), you copy serise from the global df and append the new record, then create a new dataframe df2 with the new series and save to file. However you forget to update the global df so that it does not include the new device. Next time another new device is to be added, you copy series from the global df again which does not have the previously added device, append the current new device and save to file. The file will then be overwritten without the previously added device.
To fix that you need to update the global df instead of creating a new dataframe df2. Also suggest to move the global df into MedicalDevices as a class variable:
class MedicalDevices:
# excel file path
f_path = "medical_devices_data.xlsx"
df = pd.read_excel(f_path, sheet_name="Sheet1")
...
def add_new_device(newDevice):
# update class dataframe `df` instead of creating new one
MedicalDevices.df = MedicalDevices.df.append({
"Name": newDevice.name,
"Serial": newDevice.sn,
"Manufacture": newDevice.manufacture,
"Model": newDevice.model,
"Supplier": newDevice.supplier,
"Department": newDevice.department,
"Warranty": newDevice.warranty_expire
}, ignore_index=True)
# export to file
MedicalDevices.df.to_excel(MedicalDevices.f_path, index=False)
print(f"A new device added with Serial#: {newDevice.sn}")
Related
I've been trying to call the class that is in my mainloop, but whenever I properly call it makes it that the program doesn't even launch anymore. I've been attempting to call it in order to get a method, which would return the current frame. I am aware that my code uses controller and parent to communicate between classes, but I haven't managed to fully grasp an understanding of these. If I call the mainlooped class, with "test = Database_project()", then the program won't run anymore. Can someone explain this to me? I'm trying to get the scrollbar feature to work on specific frames, and I haven't managed to figure out just yet. I'm trying to call the mainlooped class in the "CreatePage" class. Thank you in advance!
from tkinter import *
import tkinter as tk
class Database_Project(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
stack_frame_container = tk.Frame(self)
stack_frame_container.grid_columnconfigure(0, weight=1)
stack_frame_container.grid_rowconfigure(0, weight=1)
stack_frame_container.pack(side="top", fill="both", expand=True)
self.frameslist = {}
for frame in (MainPage, CreatePage):
frame_occurrence = frame.__name__
active_frame = frame(parent=stack_frame_container, controller=self)
self.frameslist[frame_occurrence] = active_frame
active_frame.grid(row=0, column=0, sticky="snew")
#self.frameslist["CreatePage"].dbproject = self.frameslist["Datanase_Project"]
self.current_frame("MainPage")
print(self.frameslist)
def current_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
active_frame.tkraise()
def get_current_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
return active_frame
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label_create = tk.Label(self, text="Create and insert data").grid(row=0, column=0, padx=50, pady=(50,0))
create_button = tk.Button(self, text="CREATE", command=lambda: controller.current_frame("CreatePage")).grid(row=1, column=0)
label_read = tk.Label(self, text="Query over data").grid(row=0, column=1, padx=50, pady=(50,0))
read_button = tk.Button(self, text="READ").grid(row=1, column=1)
label_update = tk.Label(self, text="Modify existing data").grid(row=2, column=0, padx=50, pady=(50,0))
update_button = tk.Button(self, text="UPDATE").grid(row=3, column=0, pady=(0,50))
label_delete = tk.Label(self, text="Remove data").grid(row=2, column=1, padx=50, pady=(50,0))
delete_button = tk.Button(self, text="DELETE").grid(row=3, column=1, pady=(0,50))
class CreatePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.inputlist = []
self.newinputlist = []
Test = Database_Project()
#active_frame = Test.get_current_frame("CreatePage")
#scrollbar = tk.Scrollbar(active_frame, orient="vertical")
#scrollbar.grid(row=0, column=2, stick="ns", columnspan=10)
#text["yscrollcommand"] =
labels = [tk.Label(self, text="Enter unique field"), tk.Label(self, text="Enter corresponding the value/s")]
self.inputlist.append(labels[:])
for toplabels in range(1):
self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5)
self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5)
first_entries = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
self.newinputlist.append(first_entries[:])
self.inputlist.append(first_entries[:])
for x in range(0, len(self.newinputlist) + 1):
self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5)
button_input_1 = [tk.Button(self, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self, text="BACK", command=lambda: controller.current_frame("MainPage"))]
self.inputlist.append(button_input_1[:])
button_input_2 = [tk.Button(self, text="IMPORT FILE"), tk.Button(self, text="SUBMIT DATA", command=self.submit_data)]
self.inputlist.append(button_input_2[:])
for button in range(len(self.inputlist) - 2, len(self.inputlist)):
self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5)
self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5)
def add_insert(self):
add_input = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
self.inputlist.insert(-2, add_input)
self.newinputlist.append(add_input)
for widget in self.children.values():
widget.grid_forget()
for index, widgets in enumerate(self.inputlist):
print(widgets)
widget_one = widgets[0]
widget_two = widgets[1]
print(str(index), widget_one, widget_two)
widget_one.grid(row=index, column=0, padx=10, pady=5)
widget_two.grid(row=index, column=1, padx=10)
def submit_data(self):
for index, entries in enumerate(self.newinputlist):
my_label = Label(self, text=str(entries[0].get()) + str(entries[1].get("1.0", END)))
my_label.grid(row=len(self.inputlist) + index, column=0)
if __name__ == "__main__":
NoSQL_Project = Database_Project()
NoSQL_Project.title("NoSQL Database Project")
NoSQL_Project.maxsize(500, 500)
NoSQL_Project.mainloop()
I just can't figure out how to create an option menu with items from the set in the dictionary. I have various sets, but I'm only interested in the "Individuals" set at the moment.
The instructions that I found on the Internet tell how to create an option menu with already existing items in the list or set, but my set is initially empty, and gradually fills up when the user enters a new name in this set.
I would like to know how to create an option menu that will display all the names that are in the "Individuals" set.
The final result should look like:
Please select an individual:
Name 1
Name 2
Name 3
etc.
My current option menu just displays all the sets that are in the dictionary, and not the names in the "Individuals" Set.
My code:
from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk
# This code is a simplified version of a full program code. In the original program, there is not only a list
# of individuals, but also lists of team1, team2 ... team4.
# However, now I am only interested in the problems associated with individual list,
# and I cut out all the part of the code related to team.
class CollegeApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (IndividPage, listCheckPage, counterPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(IndividPage)
self.lift()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# In this class I have created a window with entry widget to input name of individual and save it in
# eponymous set "Individual"
class IndividPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
headingTest = Label(self, text="Enter your User Name:", font="Arial 20")
headingTest.grid(row=0, column=0, pady=5, padx=5)
self.usernameEnter = Entry(self, width=40)
self.usernameEnter.grid(row=0, column=1, padx=5, pady=5)
self.TeamName = StringVar(self)
self.TeamName.set("Individual")
confirmBtn = Button(self, text="Confirm User", font="Arial 16",
command=self.confirm)
confirmBtn.config(height=4, width=12)
confirmBtn.grid(row=2, column=2, sticky=E, padx=45, pady=360)
# Checking the "add_to_team" function has been executed and moving to the next page.
def confirm(self):
if self.add_to_team():
self.controller.show_frame(listCheckPage)
# Function to check the presence of input
def add_to_team(self):
user = self.usernameEnter.get()
if len(user) == 0:
messagebox.showwarning(title='No user', message='Please enter a username!')
return
if self.usernameEnter.get():
self.controller.show_frame(listCheckPage)
team_name = self.TeamName.get()
team = teams[team_name]
team.add(user)
self.controller.frames[listCheckPage].team_listboxes[team_name].insert(END, user)
print(teams)
# Class that creates page with lists of four teams and individuals (Focusing on individuals right now)
# Also there is two buttons "Add User" and "Start Counter" to start points calculator
class listCheckPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
self.team_listboxes = {}
for col_num, teamname in enumerate(teams):
teamMembers = Listbox(self)
teamMembers.config(height=13, width=15)
teamMembers.grid(row=0, column=col_num, padx=5, pady=50, sticky=S)
for i, user in enumerate(teams[teamname]):
teamMembers.insert(i, user)
self.team_listboxes[teamname] = teamMembers
INDHeading = Label(self, text="Individuals", font="Arial 16")
INDHeading.grid(row=0, column=4, pady=0, padx=15, sticky=N)
addUserBtn = Button(self, text="Add User", font="Arial 16",
command=lambda: self.controller.show_frame(IndividPage))
addUserBtn.config(height=3, width=80)
addUserBtn.grid(row=1, column=0, columnspan=5, pady=0, sticky=N)
CounterBtn = Button(self, text="Start Counter", font="Arial 16",
command=lambda: self.controller.show_frame(counterPage))
CounterBtn.config(height=3, width=80)
CounterBtn.grid(row=2, column=0, columnspan=5, pady=0, sticky=N)
# Main problem start here
# This class creating dropdown menu (or combobox) with sets "teamX" and "Individual" but it was unplanned
# I want this combobox to show not all possible sets (team1, team2 etc.).
# Instead of that I want the combobox will show all the names that were entered in the "Individuals" set.
# I would also like to point out that the same process will be used for the sets of team1, team2 etc.
class counterPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
indivLabel = Label(self, text="Please select an individual: ", font="Arial 20")
indivLabel.grid(row=0, column=0, pady=10, padx=10)
IndivName = StringVar(self)
IndivName.set(['Individual'])
indivMenu = OptionMenu(self, IndivName, *teams)
indivMenu.grid(row=0, column=1, pady=10, padx=10)
backBtn = Button(self, text="BACK", font="Arial 16", height=2, width=6,
command=lambda: self.controller.show_frame(IndividPage))
backBtn.grid(row=7, column=0, sticky=W, pady=245, padx=10)
if __name__ == '__main__':
teams = {}
for team in range(1, 5):
teams[f'Team{team}'] = set()
teams = {'Team1': set(), 'Team2': set(), 'Team3': set(), 'Team4': set(), 'Individual': set()}
app = CollegeApp()
app.geometry("800x500")
app.resizable(False, False)
app.title('Points Counter')
app.mainloop()
My plan was to create a combobox with the names of individuals, so that user can select a name from the set and enter its points into the Entry box. I tried to do this, but my drop-down list does not show a list of names in the Individual set, but just all 5 sets: Team1, Team2, Team3, Team4 and Individuals.
Initially, everything is arranged so that the user enters the name of the Individual, which is saved in the dictionary teams = {... 'Individuals': set()} after which the names are displayed in the list box. In the next window, there should be a drop-down list with all the names that are in the set of Individuals, but, as I said above, I could not create it.
I would like to know how to solve this problem.
My code:
from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk
# This code is a simplified version of a full program code. In the original program, there is not only a list
# of individuals, but also lists of team1, team2 ... team4.
# However, now I am only interested in the problems associated with individual list,
# and I cut out all the part of the code related to team.
class CollegeApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (IndividPage, listCheckPage, counterPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(IndividPage)
self.lift()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# In this class I have created a window with entry widget to input name of individual and save it in
# eponymous set "Individual"
class IndividPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
headingTest = Label(self, text="Enter your User Name:", font="Arial 20")
headingTest.grid(row=0, column=0, pady=5, padx=5)
self.usernameEnter = Entry(self, width=40)
self.usernameEnter.grid(row=0, column=1, padx=5, pady=5)
self.TeamName = StringVar(self)
self.TeamName.set("Individual")
confirmBtn = Button(self, text="Confirm User", font="Arial 16",
command=self.confirm)
confirmBtn.config(height=4, width=12)
confirmBtn.grid(row=2, column=2, sticky=E, padx=45, pady=360)
# Checking the "add_to_team" function has been executed and moving to the next page.
def confirm(self):
if self.add_to_team():
self.controller.show_frame(listCheckPage)
# Function to check the presence of input
def add_to_team(self):
user = self.usernameEnter.get()
if len(user) == 0:
messagebox.showwarning(title='No user', message='Please enter a username!')
return
if self.usernameEnter.get():
self.controller.show_frame(listCheckPage)
team_name = self.TeamName.get()
team = teams[team_name]
team.add(user)
self.controller.frames[listCheckPage].team_listboxes[team_name].insert(END, user)
print(teams)
# Class that creates page with lists of four teams and individuals (Focusing on individuals right now)
# Also there is two buttons "Add User" and "Start Counter" to start points calculator
class listCheckPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
self.team_listboxes = {}
for col_num, teamname in enumerate(teams):
teamMembers = Listbox(self)
teamMembers.config(height=13, width=15)
teamMembers.grid(row=0, column=col_num, padx=5, pady=50, sticky=S)
for i, user in enumerate(teams[teamname]):
teamMembers.insert(i, user)
self.team_listboxes[teamname] = teamMembers
INDHeading = Label(self, text="Individuals", font="Arial 16")
INDHeading.grid(row=0, column=4, pady=0, padx=15, sticky=N)
addUserBtn = Button(self, text="Add User", font="Arial 16",
command=lambda: self.controller.show_frame(IndividPage))
addUserBtn.config(height=3, width=80)
addUserBtn.grid(row=1, column=0, columnspan=5, pady=0, sticky=N)
CounterBtn = Button(self, text="Start Counter", font="Arial 16",
command=lambda: self.controller.show_frame(counterPage))
CounterBtn.config(height=3, width=80)
CounterBtn.grid(row=2, column=0, columnspan=5, pady=0, sticky=N)
# Main problem start here
# This class creating dropdown menu (or combobox) with sets "teamX" and "Individual" but it was unplanned
# I want this combobox to show not all possible sets (team1, team2 etc.).
# Instead of that I want the combobox will show all the names that were entered in the "Individuals" set.
# I would also like to point out that the same process will be used for the sets of team1, team2 etc.
class counterPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
indivLabel = Label(self, text="Please select an individual: ", font="Arial 20")
indivLabel.grid(row=0, column=0, pady=10, padx=10)
IndivName = StringVar(self)
IndivName.set(teams['Individual'])
indivMenu = OptionMenu(self, IndivName, *teams)
indivMenu.grid(row=0, column=1, pady=10, padx=10)
backBtn = Button(self, text="BACK", font="Arial 16", height=2, width=6,
command=lambda: self.controller.show_frame(IndividPage))
backBtn.grid(row=7, column=0, sticky=W, pady=245, padx=10)
if __name__ == '__main__':
teams = {}
for team in range(1, 5):
teams[f'Team{team}'] = set()
teams = {'Team1': set(), 'Team2': set(), 'Team3': set(), 'Team4': set(), 'Individual': set()}
pointsInd = []
app = CollegeApp()
app.geometry("800x500")
app.resizable(False, False)
app.title('Points Counter')
app.mainloop()
ok, so I think I figured it out (I marked changes in the code with comments, they have a lot of "-" so that is how You will know (in total changes in 3 places (5 comments that have a lot of "-"))):
from tkinter import *
from tkinter import messagebox
import tkinter.ttk as ttk
# This code is a simplified version of a full program code. In the original program, there is not only a list
# of individuals, but also lists of team1, team2 ... team4.
# However, now I am only interested in the problems associated with individual list,
# and I cut out all the part of the code related to team.
class CollegeApp(Tk):
def __init__(self):
Tk.__init__(self)
container = ttk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
for F in (IndividPage, listCheckPage, counterPage):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(IndividPage)
self.lift()
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
if cont == counterPage: # changes here ----------------------------------------------------------------------------------------------------------
frame.userEntry() # ------------------------------------------------------------------------------
# In this class I have created a window with entry widget to input name of individual and save it in
# eponymous set "Individual"
class IndividPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
headingTest = Label(self, text="Enter your User Name:", font="Arial 20")
headingTest.grid(row=0, column=0, pady=5, padx=5)
self.usernameEnter = Entry(self, width=40)
self.usernameEnter.grid(row=0, column=1, padx=5, pady=5)
self.TeamName = StringVar(self)
self.TeamName.set("Individual")
confirmBtn = Button(self, text="Confirm User", font="Arial 16",
command=self.confirm)
confirmBtn.config(height=4, width=12)
confirmBtn.grid(row=2, column=2, sticky=E, padx=45, pady=360)
# Checking the "add_to_team" function has been executed and moving to the next page.
def confirm(self):
if self.add_to_team():
self.controller.show_frame(listCheckPage)
# Function to check the presence of input
def add_to_team(self):
user = self.usernameEnter.get()
if len(user) == 0:
messagebox.showwarning(title='No user', message='Please enter a username!')
return
if self.usernameEnter.get():
self.controller.show_frame(listCheckPage)
team_name = self.TeamName.get()
team = teams[team_name]
team.add(user)
self.controller.frames[listCheckPage].team_listboxes[team_name].insert(END, user)
print(teams)
# Class that creates page with lists of four teams and individuals (Focusing on individuals right now)
# Also there is two buttons "Add User" and "Start Counter" to start points calculator
class listCheckPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
self.userEntry()
def userEntry(self):
self.team_listboxes = {}
for col_num, teamname in enumerate(teams):
teamMembers = Listbox(self)
teamMembers.config(height=13, width=15)
teamMembers.grid(row=0, column=col_num, padx=5, pady=50, sticky=S)
for i, user in enumerate(teams[teamname]):
teamMembers.insert(i, user)
self.team_listboxes[teamname] = teamMembers
INDHeading = Label(self, text="Individuals", font="Arial 16")
INDHeading.grid(row=0, column=4, pady=0, padx=15, sticky=N)
addUserBtn = Button(self, text="Add User", font="Arial 16",
command=lambda: self.controller.show_frame(IndividPage))
addUserBtn.config(height=3, width=80)
addUserBtn.grid(row=1, column=0, columnspan=5, pady=0, sticky=N)
CounterBtn = Button(self, text="Start Counter", font="Arial 16",
command=lambda: self.controller.show_frame(counterPage))
CounterBtn.config(height=3, width=80)
CounterBtn.grid(row=2, column=0, columnspan=5, pady=0, sticky=N)
# Main problem start here
# This class creating dropdown menu (or combobox) with sets "teamX" and "Individual" but it was unplanned
# I want this combobox to show not all possible sets (team1, team2 etc.).
# Instead of that I want the combobox will show all the names that were entered in the "Individuals" set.
# I would also like to point out that the same process will be used for the sets of team1, team2 etc.
class counterPage(ttk.Frame):
def __init__(self, parent, controller):
self.controller = controller
ttk.Frame.__init__(self, parent)
# self.userEntry() this method call seems to be useless -------------------------------------------------
def userEntry(self):
indivLabel = Label(self, text="Please select an individual: ", font="Arial 20")
indivLabel.grid(row=0, column=0, pady=10, padx=10)
list_ = [] # changes here ----------------------------------------------------------------------------------------------------
for set_ in teams.values():
for name in set_:
list_.append(name)
IndivName = StringVar(self)
IndivName.set(list_[0] if len(list_) else None)
indivMenu = OptionMenu(self, IndivName, list_)
indivMenu.grid(row=0, column=1, pady=10, padx=10) # --------------------------------------------------------------------------------
backBtn = Button(self, text="BACK", font="Arial 16", height=2, width=6,
command=lambda: self.controller.show_frame(IndividPage))
backBtn.grid(row=7, column=0, sticky=W, pady=245, padx=10)
if __name__ == '__main__':
# teams = {}
# for team in range(1, 5):
# teams[f'Team{team}'] = set()
teams = {'Team1': set(), 'Team2': set(), 'Team3': set(), 'Team4': set(), 'Individual': set()}
pointsInd = []
app = CollegeApp()
# app.geometry("x500")
# app.resizable(False, False)
app.title('Points Counter')
app.mainloop()
Basically I made it so that the userEntry function gets called when user gets to that frame instead when the class is initiated which means it will upadate every time someone switches to that frame, also made it so that it displays each name individually.
Also I suggest following PEP 8 and using snake_case for function, variable and method names and using CapitalCase for class names. And I suggest following other PEP 8 rules too (they are not mandatory tho)
The entry value is just simply doesn't get passed into the function, whatever I do.
class Registration(tk.Frame):
def __init__(self, parent, controller):
name_var = tk.StringVar()
name_entry = ttk.Entry(self, textvariable=name_var)
name_entry.grid(row=1, column=1)
button1 = ttk.Button(self, text="Registration", command=self.RegFunction(name_var.get()))
button1.grid(row=4, column=4)
def RegFunction(self, name)
print(name)
Edit:
I just realized, if I add a variable to the function, it gets called as soon as I run the program, and it doesn't care about the button; but if I don't give it a variable, it works as it should, only when I push the button.
Here is the whole code
import tkinter as tk
from tkinter import ttk
class ToDoList(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (LogIn, Registration):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(LogIn)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class LogIn(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# label of frame Layout 2
label = ttk.Label(self, text="Log In")
label.grid(row=0, column=4, padx=10, pady=10)
username_label = ttk.Label(self, text="Username :")
username_label.grid(row=1, column=1)
password_label = ttk.Label(self, text="Password:")
password_label.grid(row=2, column=1)
usrname_entry = ttk.Entry(self)
usrname_entry.grid(row=1, column=2)
password_entry = ttk.Entry(self)
password_entry.grid(row=2, column=2)
button2 = ttk.Button(self, text="Registration",
command=lambda: controller.show_frame(Registration))
button2.grid(row=4, column=1, padx=10, pady=10)
class Registration(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Registration")
label.grid(row=0, column=4, padx=10, pady=10)
name_label = ttk.Label(self, text="Username:")
name_label.grid(row=1, column=0)
pass1_label = ttk.Label(self, text="Password:")
pass1_label.grid(row=2, column=0)
pass2_label = ttk.Label(self, text="Password again:")
pass2_label.grid(row=3, column=0)
name_var = tk.StringVar()
name_entry = ttk.Entry(self, textvariable=name_var)
name_entry.grid(row=1, column=1)
pass1_entry = ttk.Entry(self)
pass1_entry.grid(row=2, column=1)
pass2_entry = ttk.Entry(self)
pass2_entry.grid(row=3, column=1)
k = 12
button1 = ttk.Button(self, text="Registration", command=self.RegFunction(k))
button1.grid(row=4, column=4, padx=10, pady=10)
button2 = ttk.Button(self, text="Back to Login",
command=lambda: controller.show_frame(LogIn))
button2.grid(row=4, column=0, padx=10, pady=10)
def RegFunction(self, x):
print("Something", x)
app = ToDoList()
app.mainloop()
There's a difference between calling a function and passing in a function's name so it can be called later on. The ttk.Button command= expects a function name (or reference) to be passed in, so that the named or referenced function can be called later. You are calling the function rather than passing in its name, so things go awry.
Change:
button1 = ttk.Button(self, text="Registration", command=self.RegFunction(name_var.get()))
to:
button1 = ttk.Button(self, text="Registration", command=lambda: self.RegFunction(name_var.get()))
and you'll be closer to your goal. The lambda tells Python not to call the function but rather just return a reference to it that can be used to call it later.
Once you do that, you'll see that you have a typo in your function definition -- you're missing a colon at the end of the def statement. So, change:
def RegFunction(self, name)
to:
def RegFunction(self, name): # colon added at end
command=self.RegFunction(name_var.get()) will execute self.RegFunction() immediately. You need to use lambda:
class Registration(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
name_var = tk.StringVar()
name_entry = ttk.Entry(self, textvariable=name_var)
name_entry.grid(row=1, column=1)
button1 = ttk.Button(self, text="Registration", command=lambda: self.RegFunction(name_var.get()))
button1.grid(row=4, column=4)
def RegFunction(self, name):
print(name)
Note that your code did not call super().__init__(parent) inside __init__().
You are not calling the entry function, but the outcome of the function. Try it without the brackets:
button1 = ttk.Button(self, text="Registration", command=self.RegFunction(name_var))
Also, the RegFunction misses the colon:
def RegFunction(self, name):
print(name)
I am creating a little app that will take in details from the user about a bank transaction and then display it in a table.
I am currently using a CSV file to store the data and then when a new piece of data is put in, it will display that too.
I have a list of labels and then a button at the bottom of them to submit new entry. The problem I am getting is when the length of the table exceeds what it was to begin with there is a display error.
class accountant(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
master.title("Accountant")
self.pack()
self.numIncoming = 0
self.numOutgoing = 0
self.incoming = fs.fileStore("incoming", "csv")
print("log: incoming.csv opened sucessfully")
self.outgoing = fs.fileStore("outgoing", "csv")
print("log: outgoing.csv opened sucessfully")
self.setup()
def setup(self):
self.paint()
self.incomingData()
self.outgoingData()
self.newEntryButtons()
def paint(self):
tk.Label(self, width=45, text="incoming").grid(row=1, column=0, columnspan=45)
tk.Label(self, width=45, text="outgoing").grid(row=1, column=45, columnspan=45)
tk.Label(self, width=15, text="Date").grid(row=2, column=0, columnspan=15)
tk.Label(self, width=15, text="Transaction Name").grid(row=2, column=15, columnspan=15)
tk.Label(self, width=15, text="Amount").grid(row=2, column=30, columnspan=15)
tk.Label(self, width=15, text="Date").grid(row=2, column=45, columnspan=15)
tk.Label(self, width=15, text="Transaction Name").grid(row=2, column=60, columnspan=15)
tk.Label(self, width=15, text="Amount").grid(row=2, column=75, columnspan=15)
def incomingData(self):
self.incoming.closeFile()
self.incoming.openFile()
i = 3
for cell in self.incoming.reader:
#cell[0] = Date, cell[1]= Transaction Name, cell[2] =amount
tk.Label(self, width=15, text=cell[0]).grid(row=i, column=0, columnspan=15)
tk.Label(self, width=15, text=cell[1]).grid(row=i, column=15, columnspan=15)
tk.Label(self, width=15, text=cell[2]).grid(row=i, column=30, columnspan=15)
i += 1
self.numIncoming = i
print("incoming:", self.numIncoming-3)
print("outgoing:", self.numOutgoing-3)
def outgoingData(self):
self.outgoing.closeFile()
self.outgoing.openFile()
i = 3
for cell in self.outgoing.reader:
#cell[0] = Date, cell[1]= Transaction Name, cell[2] =amount
tk.Label(self, width=15, text=cell[0]).grid(row=i, column=45, columnspan=15)
tk.Label(self, width=15, text=cell[1]).grid(row=i, column=60, columnspan=15)
tk.Label(self, width=15, text=cell[2]).grid(row=i, column=75, columnspan=15)
i += 1
self.numOutgoing = i
print("incoming:", self.numIncoming-3)
print("outgoing:", self.numOutgoing-3, '\n\n')
def newEntryButtons(self):
if(self.numIncoming < self.numOutgoing):
tk.Button(self, text="new incoming", width=45, command=lambda: self.newEntry(self.incoming)).grid(row=self.numOutgoing, column=0, columnspan=45)
tk.Button(self, text="new outgoing", width=45, command=lambda: self.newEntry(self.outgoing)).grid(row=self.numOutgoing, column=45, columnspan=45)
else:
tk.Button(self, text="new incoming", width=45, command=lambda: self.newEntry(self.incoming)).grid(row=self.numIncoming, column=0, columnspan=45)
tk.Button(self, text="new outgoing", width=45, command=lambda: self.newEntry(self.outgoing)).grid(row=self.numIncoming, column=45, columnspan=45)
def newEntry(self, inFile):
win = tk.Toplevel()
self.newName = tk.StringVar()
self.newDate = tk.StringVar()
self.newAmount = tk.StringVar()
tk.Label(win, width=5, text="Name:").grid(row=0, column=0, columnspan=5)
tk.Entry(win, textvariable=self.newName).grid(row=0, column=5, columnspan=5)
tk.Label(win, width=5, text="date:").grid(row=1, column=0, columnspan=5)
tk.Entry(win, textvariable = self.newDate).grid(row=1, column=5, columnspan=5)
tk.Label(win, width=5, text="amount: £").grid(row=2, column=0, columnspan=5)
tk.Entry(win, textvariable=self.newAmount).grid(row=2, column=5, columnspan=5)
button = tk.Button(win, text="submit", width=5, command= lambda: self.submit(win, inFile))
button.grid(row=5, column=5, columnspan=5)
def submit(self, win, inFile):
with open(inFile.file, 'a') as f:
string= '\n'+self.newName.get() + ',' + self.newDate.get() + ',' + self.newAmount.get()
f.write(string)
if inFile.fileName == "incoming":
self.numIncoming += 1
# print("incoming:", self.numIncoming-3)
# print("outgoing:", self.numOutgoing-3)
else:
self.numOutgoing += 1
print("outgoing:", self.numOutgoing-3)
win.destroy()
self.setup()
filestore is just a class that basically opens the csv using
reader = csv.reader(open(file+'.'fileExt)) where file and fileExt are the parameters passed in.
Here is the image after a new entry. the bottom two buttons should stay as they are and the top two should be d e f in the left column and just whitespace in the right column
Replace self.pack() with self.grid(). To change a Label to Button you will, in addition, have to define a command function that executes when the Button is pressed, see the code below
bttn = tk.Button(self, text = "buttontitle", command = self.do_function )
bttn.grid(row = 14, column = 4, sticky = W)
def do_function():
print "HI"