Creating nested dictionary from 3 different CSV files - python

My aim is to take the data from 3 different CSV file and create a nested dictionary, I realize my error, but I fail to fix it.
Shall I create 3 different methods for each file to iterate the data and then create the nested dictionary, or it is something else that I have to do?
Code:
class STproject:
def __init__(self,app): #1
self.mlb=LabelFrame(app, text='Movie Recommendation Engine')
self.mlb.grid()
self.lframe3=LabelFrame(self.mlb,text="Movies/Users",background='purple')
self.lframe3.grid(row=0,column=1)
self.framebutton=Frame(self.mlb,background='pink',height=50,width=50)
self.framebutton.grid(row=0,column=0)
self.buttonsnlabels()
def buttonsnlabels(self):
self.ratingbutton=Button(self.framebutton,text='Upload movies',command=lambda :self.file1())
self.ratingbutton.grid()
self.ratingbutton=Button(self.framebutton,text='Upload ratings',command=lambda :self.file2())
self.ratingbutton.grid()
self.ratingbutton=Button(self.framebutton,text='Upload links',command=lambda :self.file3())
self.ratingbutton.grid()
def file1(self):
umovies=tkFileDialog.askopenfilename()
f=open(umovies)
self.csv_file1 = csv.reader(f)
self.dictionary()
def file2(self):
uratings=tkFileDialog.askopenfilename()
f=open(uratings)
self.csv_file2 = csv.reader(f)
self.dictionary()
def file3(self):
links=tkFileDialog.askopenfilename()
f=open(links)
self.csv_file3 = csv.reader(f)
self.dictionary()
def dictionary(self):
for line1,line2,line3 in zip(self.csv_file1,self.csv_file2,self.csv_file3):
dict={}
dict[line1]={[line2]:[line3]}
root=Tk()
root.title()
application=STproject(root)
root.mainloop()
and this is the error given:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\Lib\lib-tk\Tkinter.py", line 1547, in __call__
return self.func(*args)
File "C:/Users/Umer Selmani/Desktop/voluntarily/Voluntiraly.py", line 825, in <lambda>
self.ratingbutton=Button(self.framebutton,text='Upload movies',command=lambda :self.file1())
File "C:/Users/Umer Selmani/Desktop/voluntarily/Voluntiraly.py", line 836, in file1
self.dictionary()
File "C:/Users/Umer Selmani/Desktop/voluntarily/Voluntiraly.py", line 858, in dictionary
for line1,line2,line3 in zip(self.csv_file1,self.csv_file2,self.csv_file3):
AttributeError: STproject instance has no attribute 'csv_file2'

I would suggest to first store the selected results at a place, and process them later by another button. In the below sample i'm using StringVar to store the file paths.
class STproject:
def __init__(self, app):
self.mlb=LabelFrame(app, text='Movie Recommendation Engine')
self.mlb.grid()
self.lframe3=LabelFrame(self.mlb,text="Movies/Users",background='purple')
self.lframe3.grid(row=0,column=1)
self.framebutton=Frame(self.mlb,background='pink',height=50,width=50)
self.framebutton.grid(row=0,column=0)
self.buttonsnlabels()
self.all_vars = [StringVar() for _ in range(3)]
def buttonsnlabels(self):
self.ratingbutton=Button(self.framebutton,text='Upload movies',command=lambda:self.file(self.all_vars[0]))
self.ratingbutton.grid(row=0,column=0)
self.ratingbutton=Button(self.framebutton,text='Upload ratings',command=lambda:self.file(self.all_vars[1]))
self.ratingbutton.grid(row=1,column=0)
self.ratingbutton=Button(self.framebutton,text='Upload links',command=lambda:self.file(self.all_vars[2]))
self.ratingbutton.grid(row=2,column=0)
self.process = Button(self.framebutton,text='Process',command=self.dictionary)
self.process.grid(row=1,column=1)
def file(self, v):
result = tkFileDialog.askopenfilename()
if result:
v.set(result)
def dictionary(self):
if all(i.get() for i in self.all_vars): #process only if all 3 files are selected
with open(self.all_vars[0].get(),"r") as a, open(self.all_vars[1].get(),"r") as b, open(self.all_vars[2].get(),"r") as c:
d = {}
for line1,line2,line3 in zip(csv.reader(a),csv.reader(b),csv.reader(c)):
d[line1]={line2:line3}
root=Tk()
root.title()
application=STproject(root)
root.mainloop()
Note that I also moved the location and the name of the dict in your original code. In your code, not other it shadows the built in method dict, it also overwrites itself during each iteration of the for loop, which i think would not be what you looking for.

Related

Python tkinter passing object between callbacks

I'm trying to update a label based on a button press that does some calculations first, but I'm getting an error I don't understand.
Background: This method was working fine in previous iterations. I have not changed the code. But I did make some changes to the class by adding a controller to the initial instantiation of the ShelfDownloader class, which these methods belong to. To clarify ShelfDownloader is only called once from a different module.
Description: On the initial call of this class, it displays the self.total_books_label correctly, but when I switch shelves it chokes. UPDATE: It seems I am passing a string, but I in the comments I posted what I tried and I still don't understand the error. See the second error:
Question: Since that's probably not the problem, I'm thinking one of my calls is missing something. Any suggestions please?
I removed the widget formatting for this question. I included these bits of code to help with the Error message.
class ShelfDownloader(ctk.CTkFrame):
def __init__(self, parent, controller):
ctk.CTkFrame.__init__(self, parent)
self.parent = parent
self.controller = controller
...
def shelf_option_callback(self, parent, *args):
if not self.downloader:
sys.exit()
shelf_choice = [s for s in SHELF_METADATA if self.shelf_choice_var.get() in s['shelf_name']][0]
for k, v in shelf_choice.items():
self.downloader_dict[k] = v
updated_dict = self.downloader.update_shelf(**self.downloader_dict)
for k, v in updated_dict.items():
self.downloader_dict[k] = v
self.display_total_books_and_pages(parent)
def draw_top_panel(self):
parent = self.top_frame
LL1 = ctk.CTkLabel(parent, text="Choose shelf to download:")
# LL1.pack()
shelf_opt_menu = ctk.CTkOptionMenu(master=parent, width=170, variable=self.shelf_choice_var,
values=self.shelf_list,
command= lambda: self.shelf_option_callback(parent)
## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
## This is my problem spot. Tried adding
## command=self.shelf_option_callback(parent)
## but different error.
shelf_opt_menu.pack()
def display_total_books_and_pages(self, parent):
if hasattr(self, 'total_books_label'):
self.total_books_label.destroy()
## GUI feedback info, partially a debugging tool
_books, _pages = (self.downloader_dict['total_book_count'], self.downloader_dict['total_page_count'])
self.display_text.set(f"Shelf has {_books} books, retrieving {_pages} pages.")
logger.debug(f"{self.display_text.get()}")
logger.debug(f"display_total_books_and_pages: {type(self.display_text)}")
self.total_books_label = ctk.CTkLabel(master=parent, width=180,
text=f"{self.display_text.get()}")
self.total_books_label.pack()
My debugging returns correct and expected output after I switch the shelf, ie shelf_opt_menu:
2022-12-12 12:05:22,612, gui_download, 183: Shelf has 1624 books, retrieving 17 pages.
2022-12-12 12:05:22,612, gui_download, 184: display_total_books_and_pages: <class 'tkinter.StringVar'>
I get an error message that I'm passing a string in the code above. UPDATE: I trimmed the full error out since I confirmed that's what is happening with logger.debug(f"{type(parent)}")
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
...
File "c:\MyProjects\gr_shelf_tools\src\gui_downloader.py", line 124, in shelf_option_callback
self.display_total_books_and_pages(parent)
File "c:\MyProjects\gr_shelf_tools\src\gui_downloader.py", line 184, in display_total_books_and_pages
self.total_books_label = ctk.CTkLabel(master=parent, width=180, text=f"{self.display_text.get()}")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2591, in _setup
self.tk = master.tk
^^^^^^^^^
AttributeError: 'str' object has no attribute 'tk'
Error 2 (when I change it to a lambda call):
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\dropdown_menu.py", line 101, in <lambda>
command=lambda v=value: self._button_callback(v),
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\dropdown_menu.py", line 106, in _button_callback
self._command(value)
File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_optionmenu.py", line 381, in _dropdown_callback
self._command(self._current_value)
TypeError: ShelfDownloader.draw_top_panel.<locals>.<lambda>() takes 0 positional arguments but 1 was given
I wasn't utilizing the StringVar, self.display_text, properly. I fixed it. Here's how: I set a trace in the callback shelf_option_callback. I call self.set_display_text from the trace to change the value of self.display_text. I moved the Label creation to draw_top_frame(self) so that it always resides in the desired position and added the option textvariable=self.display_text to the Label. This did the trick.
The Label rewrites new values in the same spot when the shelf_opt_menu OptionMenu changes.
def shelf_option_callback(self, *args):
parent = self.top_frame
if not self.downloader:
sys.exit()
shelf_choice = self.get_shelf_metadata() # a dictionary of values
self.update_dictionary(**shelf_choice) # calculates new page counts based on the shelf
self.display_text.trace('w', self.set_display_text())
def draw_top_panel(self):
## Dropbox: shelf menu
parent = self.top_frame
self.initialize_downloader() # sets up a dictionary of defaults
LL1 = ctk.CTkLabel(parent, text="Choose shelf to download:")
LL1.pack(anchor=tk.NW, ipadx=10, padx=(20, 10))
shelf_opt_menu = ctk.CTkOptionMenu(master=parent, width=170,
variable=self.shelf_choice_var,
values=self.shelf_list,
command=self.shelf_option_callback)
shelf_opt_menu.pack()
## Next option menu: download increment choices
## code here ...
self.set_display_text()
self.total_books_label = ctk.CTkLabel(master=self.top_frame, width=180,
textvariable= self.display_text,
text=f"{self.display_text.get()}")
self.total_books_label.pack()
## A couple of RadioButtons
## code here
def set_display_text(self, *args):
_books, _pages = (self.downloader_dict['total_book_count'], self.downloader_dict['total_page_count'])
self.display_text.set(f"Shelf has {_books} books, retrieving {_pages} pages.")
To simplify, although in this example the choices aren't really linked to anything.
def reset_text_var(*args):
new_text = "String from somewhere"
text_var.set(new_text)
def callback(*args):
print("made it?")
text_var.trace('w', reset_text_var())
root = tk.Tk()
text_var = tk.StringVar()
parent = tk.Frame(root)
parent.pack()
choices = ['Choice 1', 'Choice 2', 'Choice 3']
text_var.set("You haven't picked anything yet")
opt_menu = tk.OptionMenu(parent, text_var, *choices, command=callback)
a_label = tk.Label(parent, textvariable=text_var, text=f"Your new string: {text_var.get()}")
opt_menu.pack()
a_label.pack()
root.mainloop()

python3 with tkinter: call widget from a function

I'm having a problem with this code:
from tkinter import *
class app:
def create(arrSettings):
proot = Toplevel()
proot.title("Settings")
m = Frame(proot).pack() #Some Frames so I can arrange them how I'd like to
mcan = Canvas(proot)
mcan.pack(fill="both", side="left")
x = Frame(proot).pack()
xcan = Canvas(proot)
xcan.pack(fill="both", expand="yes", side="left")
win_0 = Frame(xcan)
lbl_0 = Label(win_0, text="Option0").pack()
txt_0 = Text(win_0).pack()
win_0.pack()
win_1 = Frame(xcan)
lbl_1 = Label(win_1, text="Option1").pack()
txt_1 = Text(win_1).pack()
win_1.pack()
btn_menu0 = Button(mcan, text="Menu0", command=app.func_btn_menu0).pack()
btn_menu1 = Button(mcan, text="Menu1", command=app.func_btn_menu1).pack()
def func_btn_menu0():
lbl_0.config(text="foo") # <-- Problem
txt_0.insert("end", "bar") # <-- Problem
def func_btn_menu1():
pass
(I left the code for the design(bg, border, ...) out)
This is another window which will be started by the main one.
It shows some buttons on the left and some labels and textboxes on the right.
Whenever a button on the left has been pushed the text of the labels should be changed.
That's the problem: When I push a button I get this error and the text won't be changed:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.4/tkinter/__init__.py", line 1536, in __call__
return self.func(*args)
File "/[...]/program.py", line 27, in colormain
lbl_0.config(text="Background")
NameError: name 'lbl_0' is not defined
I don't really understand why this gives me an error so I'd like to ask you.
This code is being started from the main window with the code:
program.app.create(arrSettings) #arrSettings is an array in which some colors for the design are
Thanks in advance.
Do not declare and pack in the same line
Return of this peice of code is None
Label(win_0, text="Option0").pack()
whereas, this returns an object of Label class
Label(win_0, text="Option0")
so use:-
lbl_0 = Label(win_0, text="Option0")
lbl_0.pack()
instead of
lbl_0 = Label(win_0, text="Option0").pack()
Also use self object as argument to functions. Check that the variables are in scope wherever you are using it.
This should help you get through this error...

AttributeError: 'NoneType' object has no attribute 'grid_remove'

I have only done a little work with Tkinter and I enjoy using it but as with any type programing it takes time to learn. I am trying to create a simple To do list that will eventually be saved on a file. But i can't get the button in line 17 to be removed and the on the next line be replace in a different position.
from tkinter import *
import time
root = Tk()
root.geometry("300x300")
root.title("Programs")
global TDrow
TDrow = 2
def tdTaskAdd():
global TDrow
global tdEnter
TDrow = int(TDrow+1)
s = tdEntry.get()
label = Label(ToDoFrame,text=s).grid(row=TDrow,column=1)
tdEntry.grid(row=TDrow+1,column=1)
tdEnter.grid_remove()
tdEnter = Button(ToDoFrame,text="AddTask",command=tdTaskAdd).grid(row=TDrow+2,column=1)
ToDoFrame = Frame()
ToDoFrame.place(x=0,y=10)
tdTitle = Label(ToDoFrame,text="To Do List:").grid(row=TDrow-1,column=1)
tdEntry= Entry(ToDoFrame)
tdEntry.grid(row=TDrow+1,column=1)
tdEntry.insert(0, "Enter a new task")
global tdEnter
tdEnter = Button(ToDoFrame,text="Add Task",command=tdTaskAdd).grid(row=TDrow+2,column=1)
mainloop()
I keep getting an error when running this saying that:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "C:\Users\Eddy\Desktop\pythonStartup.py", line 17, in tdTaskAdd
tdEnter.grid_remove()
AttributeError: 'NoneType' object has no attribute 'grid_remove'
The problem is this line:
tdEnter = Button(ToDoFrame,text="Add Task",command=tdTaskAdd).grid(row=TDrow+2,column=1)
This way, tdEnter is not the Button, but the return value of grid, i.e. None.
Try this instead:
tdEnter = Button(ToDoFrame,text="Add Task",command=tdTaskAdd)
tdEnter.grid(row=TDrow+2,column=1)
Same for label and when you create a new button in your tdAddTask function.
BTW, no need to add a new button each time, just call it's grid method to repositon it.

How can I create NEW Listboxes with a Button and collect all the data with a final submit Button in TKinter?

Okay so here is my problem. I am trying to create a very open ended user friendly Gui out of Tkinter. In short I made a button a function STAGE that creates a Listbox that has choose-able indexes. Then I can press the button SUBMIT that will print the selected keys.
BUT if I press ADD STAGE to make another listbox of the same value I CANNOT go back and edit or retrieve the selected values of the old listbox.
I understand that this is because of the listbox will have the same name so.....
from Tkinter import *
import tkMessageBox
class Insert_page(Frame):
global i0
global listbox
global Tech_option
Tech_option=['Rolled Plate','extrude bar','as-cast','wire','Other','USER Details']
i0=-1
def __init__(self,parent):
Frame.__init__(self,parent,background="white")
self.parent=parent
self.initUI()
def initUI(self):
self.parent.title("material Gui v2")
self.grid(row=1,column=1)
mButton=Button(self,text='Start Stages',command=self.stage).grid(row=2,column=5,sticky=W)
mButton3=Button(self,text='submit',command=self.submit).grid(row=9,column=1,sticky=W)
def stage(self): ######################HERE IS THE PROBLEM#########
global i0
i0+=1
stageFrame=Frame(self,bd=1,bg='red',relief=SUNKEN)
stageFrame.grid(row = 1+5*i0, column = 1, rowspan = 5, columnspan = 7, sticky = W+E+N+S)
stageVar = StringVar()
OPTIONS = [""]+range(0,10)
w = apply(OptionMenu, (stageFrame, stageVar) + tuple(OPTIONS))
w.grid(row=1,column=1)
stageLabel=Label(stageFrame,text='Stage')
stageLabel.grid(row=1+5*i0,column=0,sticky=W)
mButton=Button(stageFrame,text='add Stage',command=self.stage).grid(row=9,column=1,sticky=W)
listbox = Listbox(stageFrame,selectmode= MULTIPLE,exportselection=False) ######REPLACED CODE######
listbox.grid(row=3+5*i0,column=2)
for item in Tech_option :
listbox.insert(END, item)
mButton3=Button(self,text='submit',command=lambda: self.submit(listbox,Tech_option)).grid(row=9+5*i0,column=1,sticky=W)
######REPLACED CODE######
def submit(self,lb,option_list):
Tech_select=[]
for i in list(lb.curselection()):
Tech_select.append(Tech_option[int(i)])
print Tech_select
def main():
mGui= Tk()
mGui.geometry('800x600+200+200')
menubar=Menu(mGui)
filemenu=Menu(menubar,tearoff=0)
filemenu.add_command(label="New")
filemenu.add_command(label="Open")
filemenu.add_command(label="SaveAs...")
filemenu.add_command(label="Close")
menubar.add_cascade(label='File',menu=filemenu)
mGui.config(menu=menubar)
app=Insert_page(mGui)
mGui.mainloop()
main()
I replaced it with this...
exec ('listbox_%s = Listbox(stageFrame,selectmode= MULTIPLE,exportselection=False)' % (i0)) in globals(), locals()
exec ('listbox_%s.grid(row=3+5*i0,column=2)' % (i0)) in globals(), locals()
for item in Tech_option :
exec ('listbox_%s.insert(END, item)' % (i0)) in globals(), locals()
mButton3=Button(self,text='submit',command=lambda: self.submit(eval('listbox_%s'%(i0)),Tech_option)).grid(row=9+5*i0,column=1,sticky=W)
What it should do is create NEW listbox variables but all I get back when pressing submit is.
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1413, in __call__
return self.func(*args)
File "/home/xxxxxx/pythons/xxxxxxxx.py", line 78, in <lambda>
mButton3=Button(self,text='submit',command=lambda: self.submit(eval('listbox_%s'%(i0)),Tech_option)).grid(row=9+5*i0,column=1,sticky=W)
File "<string>", line 1, in <module>
NameError: name 'listbox_1' is not defined
If someone could help me with this that would be great.
You need list of listbox to keep all lists from all stages.
Now in listbox you have only list from last created stage.
You will have to use lines like this (in different places)
self.all_listboxes = []
#---
self.all_listboxes.append( listbox )
#---
for one_list in self.all_listboxes:
for x in one_list.curselection():
BTW: use self.listbox (in __init__) in place of global listbox.
The same with i0 and Tech_option.
We use class and self to not use global

Python Attribute Error: object has no attribute 'self'

I have a problem in class inheritance in Python; maybe it's not related to inheritance, but I have no other idea. I'm working with selenium web-driver. Here's the code I use:
from selenium import webdriver
class UIInterface(object):
def __init__(self):
self.driver = webdriver.Ie()
self.driver.get('URL')
def parentMethod(self, itemClassName = ''):
allElements = self.getAllElements()
return [el for el in allElements if el.get_attribute('type') == 'checkbox']
def getAllElements(self):
return self.driver.find_elements_by_tag_name('input')
class InterfaceChild(UIInterface):
def __init__(self):
super(InterfaceChild, self).__init__()
def childMethod(self):
returnedList = self.parentMethod(itemClassName = 'SomeClassName')
for item in returnedList:
print item.get_attribute('innerHTML')
This code gives me an error for line returnedList = self.parentMethod(itemClassName = 'SomeClassName'); the error description is this:
(<type 'exceptions.AttributeError'>, AttributeError("'InterfaceChild' object has no attribute 'self'",), <traceback object at 0x000000000418E5C8>)
I thought it might be related to inheritance, so I tried to put parentMethod and getAllElements in the class InterfaceChild; same exception raised. Any idea about this??
EDIT 1:
This is the main method for making instance of classes:
if __name__ == '__main__':
ieInterface = InterfaceChild()
ieInterface.childMethod()
This is the complete stack-trace:
Traceback (most recent call last):
File "C:\Program Files\Eclipse\eclipse-jee-helios-win32\eclipse-jee-helios-win32\plugins\org.python.pydev_2.8.2.2013090511\pysrc\pydevd.py", line 1446, in <module>
debugger.run(setup['file'], None, None)
File "C:\Program Files\Eclipse\eclipse-jee-helios-win32\eclipse-jee-helios-win32\plugins\org.python.pydev_2.8.2.2013090511\pysrc\pydevd.py", line 1092, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "D:\workspace\testCode.py", line 133, in main
ieInterface.childMethod()
File "D:\workspace\testCode.py", line 33, in childMethod
returnedList = self.parentMethod(itemClassName = 'SomeClassName')
File "D:\workspace\testCode.py", line 45, in parentMethod
allElements = self.getAllElements()
AttributeError: 'InterfaceChild' object has no attribute 'self'
I installed selenium with pip under Python 2.7 and changed the code to use Chrome driver[1] instead and changed the checker to make sure there would be some input tags being found. full code below. I'm running the code on Mac OS X. it works just fine.
So I guess the issue here is not the code. (Windows is not a friend of Python, perhaps :).
from selenium import webdriver
class UIInterface(object):
def __init__(self):
self.driver = webdriver.Chrome()
self.driver.get('http://duckduckgo.com')
def parentMethod(self, itemClassName = ''):
allElements = self.getAllElements()
result = [el for el in allElements]
print('===', result)
return result
def getAllElements(self):
return self.driver.find_elements_by_tag_name('input')
class InterfaceChild(UIInterface):
def __init__(self):
super(InterfaceChild, self).__init__()
def childMethod(self):
returnedList = self.parentMethod(itemClassName = 'SomeClassName')
for item in returnedList:
print item.get_attribute('innerHTML')
if __name__ == '__main__':
ieInterface = InterfaceChild()
ieInterface.childMethod()
The output looks like:
('===', [<selenium.webdriver.remote.webelement.WebElement object at 0x10e145cd0>, <selenium.webdriver.remote.webelement.WebElement object at 0x10e145d10>, <selenium.webdriver.remote.webelement.WebElement object at 0x10e145d50>])
[1] http://chromedriver.storage.googleapis.com/index.html?path=2.9/

Categories