Pickling dictionary data then loading it doesn't work - python

I am making a client list and I am using a dictionary and pickle to save and store the clients name and age. Unfortunately my save function overwrites any existing users and then my __init__ function doesn't display the pickled data in the dictionary just the ones I have typed in myself. Also the add function doesn't display the label with the client name on it. Can you please help with the pickle problems because I need an answer fast!
Thanks in advance!
from Tkinter import *
import pickle
root=Tk()
dic={}
class Launch(object):
def __init__(self, root):
root.withdraw()
root=Toplevel(root)
root.title("Choose Client")
self.row=3
for key in dic:
Label(root, text=key).grid(row=row, column=0)
self.row+=1
l3=Label(root, text="Client Name")
l3.grid(row=0, column=0)
self.e1=Entry(root)
self.e1.grid(row=0, column=1)
l4=Label(root, text="Client age")
l4.grid(row=1, column=0)
self.e2=Entry(root)
self.e2.grid(row=1, column=1)
b3=Button(root, text="Create client", command=self.add)
b3.grid(row=2)
def add(self):
client=self.e1.get()
age=self.e2.get()
dic[client]=age
Label(root, text="%s" % (client)).grid(row=self.row)
with open("data", "w") as f:
pickle.dump(dic, f)
def load(self):
dic=pickle.load(open("data", "rb"))
app=Launch(root)
root.mainloop()

The load method is not used at all. The following is modified code to use the load.
from Tkinter import *
import pickle
root=Tk()
dic={}
class Launch(object):
def __init__(self, root):
self.load() # <-- load
root.title("Choose Client")
self.row = 3
for key in dic:
Label(root, text=key).grid(row=self.row, column=0)
self.row += 1
l3=Label(root, text="Client Name")
l3.grid(row=0, column=0)
self.e1=Entry(root)
self.e1.grid(row=0, column=1)
l4=Label(root, text="Client age")
l4.grid(row=1, column=0)
self.e2=Entry(root)
self.e2.grid(row=1, column=1)
b3=Button(root, text="Create client", command=self.add)
b3.grid(row=2)
def add(self):
client=self.e1.get()
age=self.e2.get()
dic[client]=age
Label(root, text=client).grid(row=self.row)
self.row += 1 # <--- increase row count
with open("data", "wb") as f:
pickle.dump(dic, f)
def load(self):
# should be declared, otherwise, will create local variable
global dic
try:
dic = pickle.load(open("data", "rb"))
except IOError: # if file does not exist.
pass
app = Launch(root)
root.mainloop()
Following is another version that use instance variable instead of global variable:
from Tkinter import *
import pickle
class Launch(object):
def __init__(self, root):
self.load() # <-- load
root.title("Choose Client")
self.row = 3
for key in self.dic:
Label(root, text=key).grid(row=self.row, column=0)
self.row += 1
l3 = Label(root, text="Client Name")
l3.grid(row=0, column=0)
self.e1 = Entry(root)
self.e1.grid(row=0, column=1)
l4 = Label(root, text="Client age")
l4.grid(row=1, column=0)
self.e2 = Entry(root)
self.e2.grid(row=1, column=1)
b3 = Button(root, text="Create client", command=self.add)
b3.grid(row=2)
def add(self):
client = self.e1.get()
age = self.e2.get()
self.dic[client] = age
Label(root, text=client).grid(row=self.row)
self.row += 1 # <--- increase row count
with open("data", "wb") as f:
pickle.dump(self.dic, f)
def load(self):
try:
self.dic = pickle.load(open("data", "rb"))
except IOError:
self.dic = {}
root = Tk()
app = Launch(root)
root.mainloop()

Related

Getting index by name

I am currently on my first somewhat To Do App and i have trouble with deleting things. I dont actually understand how to properly get the value to search it by index.
import json
import tkinter as tk
class JsonArrayStore:
def __init__(self, filename):
self.filename = filename
self.__load()
def __load(self):
try:
f = open(self.filename, 'r')
self.info = json.load(f)
f.close()
except FileNotFoundError:
self.info = []
def __save(self):
with open(self.filename, 'w') as f:
json.dump(self.info, f)
def append(self, data, force_save=True):
self.info.append(data)
if force_save:
self.__save()
def remove(self, index, force_save=True):
del self.info[index]
if force_save:
self.__save()
class ToDoGUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("Don't Forget!")
self.geometry('600x400+200+100')
self.configure(bg='#eae5e1')
self.logo_bg = tk.Frame(master=self, height=20, width=3000)
self.logo_bg.pack()
self.logo_text = tk.Label(master=self.logo_bg, bg = '#599D9C',
width=3000,
text="Kelpie's To Do App!",
font=('Impact', 45), fg='white')
self.logo_text.pack()
self.add_button_frame = tk.Frame(master=self, bg='#eae5e1')
self.add_button_frame.pack(fill='x')
self.label_border = tk.Label(master=self.add_button_frame, bg='#eae5e1',
bd=12, width='500')
self.label_border.pack()
self.add_btn = tk.Button(master=self.label_border, text='Add Task!', command=self.on_button)
self.add_btn.pack(side='right', padx=10)
# Later: Add a placeholder func
self.add_text = tk.Entry(master=self.label_border)
self.add_text.insert(0, '')
self.add_text.pack(side='left', padx=10)
# Frames for tasks
self.task_frame = tk.Frame(master=self)
self.task_frame.pack()
# load from json file
a = JsonArrayStore('test1.json')
for i in a.info:
add_task_label = tk.Label(master=self, bg='red', text=f'{i}')
add_task_label.pack()
delete_btn = tk.Button(self, bg='green', text='Delete', command=self.del_btn)
delete_btn.pack()
def on_button(self):
a = JsonArrayStore('test1.json')
a.append(self.add_text.get())
task = tk.Label(master=self, bg='red', text=f'{self.add_text.get()}')
task.pack()
delete_btn = tk.Button(self, bg='green', text='Delete', command=self.del_btn)
delete_btn.pack()
def del_btn(self):
a = JsonArrayStore('test1.json')
x = a.info.index()
test = ToDoGUI()
test.mainloop()
This can be done by using enumerate to get the index in the for-loop and then using a lambda as command for the delete button which stores the index. Roughly:
for idx, i in enumerate(a.info):
add_task_label = tk.Label(master=self, bg='red', text=f'{i}')
add_task_label.pack()
delete_btn = tk.Button(self, bg='green', text='Delete', command=lambda idx=idx: self.del_btn(idx))
delete_btn.pack()
...
def del_btn(self, idx):

adding date to tkinter data entry

I'm trying to make a gui of a small project. I want to add the ability to once hitting add_item to add a data entry, it adds a time stamp to it as well. I'm very new to tkinter so any help would be great. I tried something like this:
tk.Button(root, text='add_item', command=lambda :text.insert("end", time.strftime("%d/%m/%Y%H:%M:%S")+'\n'))
b2.pack(side=tkinter.LEFT)
but couldn't get it to work with the rest of the code. I'm assuming it needs a lambda function, not sure how to set it up though.
import tkinter as tk
from datetime import datetime
import time
#1
now = datetime.now()
print ("Current date and time : ")
print (now.strftime("employee-attendance-list-%Y-%m-%d.csv"))
filename = datetime.now().strftime('employee-attendance-list-%Y-%m-%d.csv')
def dt_ins():
e.insert(0, time.asctime())
root = tk.Tk()
root.title("List App")
root.geometry("400x400")
def handler(e):
label= Label(win, text= "You Pressed Enter")
label.pack()
def retrievedata():
''' get data stored '''
global list_data
list_data = []
try:
with open (filename, "r", encoding="utf-8") as file:
for f in file:
listbox.insert(tk.END, f.strip())
list_data.append(f.strip())
print(list_data)
except:
pass
def reload_data():
listbox.delete(0, tk.END)
for d in list_data:
listbox.insert(0, d)
def add_item(event=1):
global list_data
if content.get() != "":
listbox.insert(tk.END, content.get())
list_data.append(content.get())
content.set("")
def delete():
global list_data
listbox.delete(0, tk.END)
list_data = []
def delete_selected():
try:
selected = listbox.get(listbox.curselection())
listbox.delete(listbox.curselection())
list_data.pop(list_data.index(selected))
# reload_data()
# # listbox.selection_clear(0, END)
listbox.selection_set(0)
listbox.activate(0)
listbox.event_generate("<<ListboxSelect>>")
print(listbox.curselection())
except:
pass
def quit():
global root
with open (filename, "w", encoding="utf-8") as file:
for d in list_data:
file.write(d + "\n")
root.destroy()
# LISTBOX
content = tk.StringVar()
entry = tk.Entry(root, textvariable=content)
entry.pack()
button = tk.Button(root, text="Add Item", command=lambda: [add_item(), dt_ins()])
button.pack()
entry.bind("<Return>", add_item)
button_delete = tk.Button(text="Delete", command=delete)
button_delete.pack()
button_delete_selected = tk.Button(text="Delete Selected", command=delete_selected)
button_delete_selected.pack()
listbox = tk.Listbox(root)
listbox.pack()
entry.bind("<Return>,", add_item)
bquit = tk.Button(root, text="Quit and save", command=quit)
bquit.pack()
retrievedata()
root.mainloop()

How to return entry value from another class?

I am trying to return a value from an Entry widget from another class.
My idea is, when the user has logged in successfully, the welcome screen will show the username that has just logged in.
I have tried using this:
self.userLogged = Label(main, text = self.entry_username.get())
self.userLogged.pack()
i tried linking >self.entry.entry_username.get() from the login class. But here is the error code:
AttributeError: 'App' object has no attribute 'entry_username'
Where I'm I going wrong?
Here is the full code:
from tkinter import *
import tkinter.ttk as ttk
class App():
def __init__(self,master):
notebook = ttk.Notebook(master)
notebook.pack(expand = 1, fill = "both")
#Frames
main = ttk.Frame(notebook)
notebook.add(main, text='Welcome Screen')
self.userLogged = Label(main, text = self.entry_username.get())
self.userLogged.pack()
###################################################################################################################################
##USERS##
###################################################################################################################################
class login(Frame):
def __init__(self, master):
super().__init__(master)
self.label_username = Label(self, text="Username: ",font=("bold",16))
self.label_password = Label(self, text="Password: ",font=("bold",16))
self.entry_username = Entry(self, font = ("bold", 14))
self.entry_password = Entry(self, show="*", font = ("bold", 14))
self.label_username.grid(row=0, sticky=E)
self.label_password.grid(row=1, sticky=E)
self.entry_username.grid(row=0, column=1)
self.entry_password.grid(row=1, column=1)
self.logbtn = Button(self, text="Login", font = ("bold", 10), command=self._login_btn_clicked)
self.logbtn.grid(columnspan=2)
self.pack()
def _login_btn_clicked(self):
# print("Clicked")
username = self.entry_username.get()
password = self.entry_password.get()
# print(username, password)
account_list = [line.split(":", maxsplit=1) for line in open("passwords.txt")]
# list of 2-tuples. Usersnames with colons inside not supported.
accounts = {key: value.rstrip() for key, value in account_list}
# Convert to dict[username] = password, and slices off the line ending.
# Does not support passwords ending in whitespace.
if accounts[username] == password:
self.label_username.grid_forget()
self.label_password.grid_forget()
self.entry_username.grid_forget()
self.entry_password.grid_forget()
self.logbtn.grid_forget()
self.pack_forget()
app = App(root)
else:
print("error")
root = Tk()
root.minsize(950, 450)
root.title("test")
lf = login(root)
root.mainloop()
When working with multiple classes in tkinter it is often a good idea to use class inherancy for the main window and frames. This allows us to use self.master to interact between classes.
That said you have a few things to change. You are using self where it is not needed and you should be doing import tkinter as tk to prevent overwriting of methods.
I have added a class to your code so we use one class for the root window. Then use one class for the login screen and then use one class for the frame after login.
import tkinter as tk
import tkinter.ttk as ttk
class NotebookFrame(tk.Frame):
def __init__(self, username):
super().__init__()
notebook = ttk.Notebook(self)
notebook.pack(expand=1, fill="both")
main = ttk.Frame(notebook)
notebook.add(main, text='Welcome Screen')
tk.Label(main, text=username).pack()
class Login(tk.Frame):
def __init__(self):
super().__init__()
tk.Label(self, text="Username: ", font=("bold", 16)).grid(row=0, sticky='e')
tk.Label(self, text="Password: ", font=("bold", 16)).grid(row=1, sticky='e')
self.entry_username = tk.Entry(self, font=("bold", 14))
self.entry_password = tk.Entry(self, show="*", font=("bold", 14))
self.entry_username.grid(row=0, column=1)
self.entry_password.grid(row=1, column=1)
tk.Button(self, text="Login", font=("bold", 10), command=self.master._login_btn_clicked).grid(columnspan=2)
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("test")
self.minsize(950, 450)
self.login_frame = Login()
self.login_frame.pack()
def _login_btn_clicked(self):
username = self.login_frame.entry_username.get()
password = self.login_frame.entry_password.get()
account_list = [line.split(":", maxsplit=1) for line in open("passwords.txt")]
accounts = {key: value.rstrip() for key, value in account_list}
if accounts[username] == password:
self.login_frame.destroy()
NoteFrame = NotebookFrame(username)
NoteFrame.pack()
else:
print("error")
if __name__ == "__main__":
App().mainloop()
Firstly, you should change class name from class login(Frame) to class Login(Frame).
Before fixing it, you called the login function from App, but you need to call Login class and use it.
class App:
def __init__(self, master):
notebook = ttk.Notebook(master)
notebook.pack(expand=1, fill="both")
# Frames
main = ttk.Frame(notebook)
notebook.add(main, text='Welcome Screen')
# `entry_username.get()` method is owned by the Login class,
# so you need to call from not `self(App class)` but `login(Login class)`.
login = Login(master) # Call Login class
self.userLogged = Label(main, text=login.entry_username.get())
self.userLogged.pack()
With this fix, I could call the Welcome screen.

tkinter .get() returns nothing when starting the class outside the script

I have a problem with tkinter`s .get() method. I use a simple tkinter menu to add the 9'th value to a csv while recording. While running a class for this menu from a script where the class was written, it works just fine. However, when trying to call this app from other script, I get no data appended. So, how to receive the data from .get() method while working with this menu in the other script? Here is a sample of my code (this is a part of an app and a way I start it from a script where it is located):
from myo_interface import MyoRaw
import csv
import tkinter.messagebox
import sys
import pandas as pd
class recorderForData(tkinter.Tk):
def __init__(self):
tkinter.Tk.__init__(self)
self.counter = 0
self.doTick = True
self.title("Data recording")
self.m = MyoRaw(sys.argv[1] if len(sys.argv) >= 2 else None)
self.v = tkinter.StringVar()
self.v.set("")
self.entry = tkinter.Entry(self, textvariable=self.v)
self.protocol("WM_DELETE_WINDOW", self.on_closing)
self.B1 = tkinter.Button(self, text="Start recording", command=self.process_emg)
self.B2 = tkinter.Button(self, text="Stop recording", command=self.stop_recording)
self.B3 = tkinter.Button(self, text="Clean csv", command=self.clear_csv)
self.rows_number = tkinter.Label(self, text=self.counter)
def process_emg(self, emg, times):
name = self.v.get()
with open('own_test.csv', 'a+', newline='') as file:
writing = csv.writer(file)
writing.writerow(emg+(name,))
def start_app(self):
self.entry.grid(row=0, column=0)
self.B1.grid(row=1, column=0, sticky='nsew')
self.B2.grid(row=1, column=1, sticky='nsew')
self.B3.grid(row=1, column=2, sticky='nsew')
self.rows_number.grid(row=0, column=1, sticky='nsew')
self.mainloop()
def main():
rd = recorderForData()
rd.start_app()
if __name__ == '__main__':
main()
And this is how I call it from the outside:
import test_record
def recordData():
rd = test_record.recorderForData()
rd.start_app()
So, the problem was in v.get(). Changing it to entry.get() solves it.

Python Tkinter error: "Label has no __call__method"

Im trying to create a Python tkinter login registeration but running into a small issue.
The error message is:
self.Label_Name = Label(top, text="What is your username: ")
AttributeError: Label instance has no __call__ method
Please can you proof read my code:
from Tkinter import *
class Register:
def __init__(self, parent):
top = self.top = Toplevel(parent)
# Variables to store the entries
self.VarEntUser = StringVar()
self.VarEntPass = StringVar()
self.VarEntRetype = StringVar()
self.Label_Name = Label(top, text="What is your username: ")
self.Label_Password = Label(top, text="Enter a password: ")
self.Label_Retype = Label(top, text="Retype Password: ")
# Entry fields for the user to enter there details
self.Ent_Name = Entry(top, textvariable=self.VarEntUser)
self.Ent_Password = Entry(top, textvariable=self.VarEntPass)
self.Ent_Retype = Entry(top, textvariable=self.VarEntRetype)
# Puts all the fields ^, into the window
self.Label_Name.grid(row=0, sticky=W)
self.Label_Password.grid(row=1, sticky=W)
self.Label_Retype.grid(row=2, sticky=W)
self.Ent_Password.grid(row=1, column=1)
self.Ent_Retype.grid(row=2, column=1)
self.Ent_Name.grid(row=0, column=2)
# Run the RegisterCheck function
# submit button which Checks the Entered details then writes the user and pass to a .txt file
self.MySubmitButton = Button(top, text='Submit', command=RegisterCheck)
self.MySubmitButton.pack()
self.U = raw_input(self.VarEntUser.get())
self.P = raw_input(self.VarEntPass.get())
self.R = raw_input(self.VarEntRetype.get())
class LogIn:
def __init__(self, parent):
top = self.top = Toplevel(parent)
self.a = StringVar()
self.b = StringVar()
self.Label_Log_User1 = Label(top, text='Username:')
self.Label_Log_Pass = Label(top, text='Password: ')
self.Ent_User_Log = Entry(top, textvariable=self.a)
self.Ent_Pass_Log = Entry(top, textvariable=self.b)
self.Label_Log_User1.grid(row=1)
self.Pass_Log.grid(row=2)
self.EntUserLog.grid(row=1, column=1)
self.EntPassLog.grid(row=2, column=1)
self.User = raw_input(self.EntUserLog.get())
self.Pass = raw_input(self.EntUserLog.get())
# runs the 'LoginCheck' function
self.LogInButton = Button(top, text="Log In", command=LogInCheck)
self.LogInButton.pack()
def LogInCheck(self):
# Checks if the fields are blanking displaying an error
if len(self.User) <= 0 and len(self.Pass) <= 0:
print "Please fill in all fields."
else:
pass
# Checks to see if the user and pass have been created
if self.User in 'username.txt' and self.Pass in 'password':
print 'You are now logged in!'
else:
print "Log in Failed"
def RegisterCheck(self):
# Checks if the fields are blank
if len(self.P) <= 0 and len(self.U) <= 0:
print "Please fill out all fields."
else:
pass
# Check is the password and the retype match
if self.P == self.R:
pass
else:
print "Passwords do not match"
# After registering write the user and pass to a .txt file
with open('username.txt', 'a') as fout:
fout.write(self.U + '\n')
with open('password.txt', 'a') as fout:
fout.write(self.P + '\n')
# Depending on what the user chooses, either log in or register than opens the specific window
def launch_Register():
inputDialog = Register(root)
root.wait_window(inputDialog.top)
def launch_LogIn():
inputdialog2 = LogIn(root)
root.wait_window(inputdialog2.top)
root = Tk()
label = Label(root, text='Choose an option')
label.pack()
loginB = Button(root, text='Log In', command=launch_LogIn)
loginB.pack()
registerB = Button(root, text='Register', command=launch_Register)
registerB.pack()
root.mainloop()
The problem is that in this line
Label = Label(root, text='Choose an option')
you define a Label called Label, thus shadowing the Label constructor. Then, then you create the several labels in your Register and Login classes (triggered by those two buttons), the name Label is no longer bound to the constructor, but to that specific label.
Change the name of the label, then it should work. Also, I would advise you to use lower-case names for variables and methods. This alone might help prevent many such errors.
root = Tk()
label = Label(root, text='Choose an option')
label.pack()
loginB = Button(root, text='Log In', command=launch_LogIn)
loginB.pack()
registerB = Button(root, text='Register', command=launch_Register)
registerB.pack()
root.mainloop()
Note that there are a few many more problems with your code:
StringVar a and b should probably be self.a and self.b
You are trying to use raw_input to get the user input in the Entry widgets; this is wrong! Instead, just read the value of the variables to get the values, e.g. instead of self.User, use self.a.get()
do not mix grid and pack layout
if self.User in 'username.txt' will not check whether that name is in that file
loginCheck and registerCheck should be methods of the respective class
Once I'm at it, here's (part of) my version of your code, to help you getting started:
class Register:
def __init__(self, parent):
top = self.top = Toplevel(parent)
self.var_user = StringVar()
self.var_pass = StringVar()
self.var_retype = StringVar()
Label(top, text="What is your username: ").grid(row=0, sticky=W)
Label(top, text="Enter a password: ").grid(row=1, sticky=W)
Label(top, text="Retype Password: ").grid(row=2, sticky=W)
Entry(top, textvariable=self.var_user).grid(row=0, column=1)
Entry(top, textvariable=self.var_pass).grid(row=1, column=1)
Entry(top, textvariable=self.var_retype).grid(row=2, column=1)
Button(top, text='Submit', command=self.registerCheck).grid(row=3)
def registerCheck(self):
u, p, r = self.var_user.get(), self.var_pass.get(), self.var_retype.get()
if p and u:
if p == r:
logins[u] = p
else:
print "Passwords do not match"
else:
print "Please fill out all fields."
class LogIn:
# analogeous to Register; try to figure this out xourself
def launch_Register():
inputDialog = Register(root)
root.wait_window(inputDialog.top)
def launch_LogIn():
inputDialog = LogIn(root)
root.wait_window(inputDialog.top)
logins = {}
root = Tk()
Label(root, text='Choose an option').pack()
Button(root, text='Log In', command=launch_LogIn).pack()
Button(root, text='Register', command=launch_Register).pack()
root.mainloop()
Note that I changed the login "database" from files to a dictionary to keep things simple and to focus on the Tkinter problems. Of course, neither a simple dictionary nor a plain-text file is an appropriate way to store login information.
Also, I put the creation and the layout of the GUI widgets on one line. In this case this is possible since we do not need a reference to those widgets, but beware never to do e.g. self.label = Label(...).grid(...), as this will bind self.label to the result of grid, and not to the actual Label.
Finally, this will still print all the messages to the standard output. Instead, you should add another Label for that, or open a message dialogue, but this is left as an excercise to the reader...

Categories