How to add columns to a tkinter.Listbox? - python-3.x

I am trying to add these panda columns to a Listbox, so they read like this:
New Zealand NZD
United States USD
ETC.
I am using pandas to get the data from a .csv, but when I try and use a for loop to add the items to the list box using insert I get the error
NameError: name 'END' is not defined or
NameError: name 'end' is not defined
Using this code:
def printCSV():
csv_file = ('testCUR.csv')
df = pd.read_csv(csv_file)
print (df[['COUNTRY','CODE']])
your_list = (df[['COUNTRY','CODE']])
for item in your_list:
listbox.insert(end, item)

You could turn the csv file into a dictionary, use the combined country and currency codes as the keys and just the codes as the values, and finally insert the keys into the Listbox. To get the code of the current selection, you can do this: currencies[listbox.selection_get()].
listbox.selection_get() returns the key which you then use to get the currency code in the currencies dict.
import csv
import tkinter as tk
root = tk.Tk()
currencies = {}
with open('testCUR.csv') as f:
next(f, None) # Skip the header.
reader = csv.reader(f, delimiter=',')
for country, code in reader:
currencies[f'{country} {code}'] = code
listbox = tk.Listbox(root)
for key in currencies:
listbox.insert('end', key)
listbox.grid(row=0, column=0)
listbox.bind('<Key-Return>', lambda event: print(currencies[listbox.selection_get()]))
tk.mainloop()

Related

How can i catch any error caused by unset module variable?

So I have this function list and showlist but i need to catch any error caused by an unset module variable. can someone help me whats the first thing i should do?
this is my code at the moment
import csv
filep #filepath
menulist = []
def list():
"""Function to read the csv file, create a nested list
and return the list that is sorted based on calories
in the ascending order."""
global menulist
menulist = [] #store items
with open(filep) as csv_file: #read file
reader = csv.reader (csv_file, delimiter=',')
next(reader, None)
for row in reader:
row[2] = int(row[2].strip())
row[1] = float(row[1].strip())
if row[2]> 100 and row[2] <200:
menulist.append(row)
menulist.sort(key=lambda x: x[-1])
def show_list():#Function to display menu
global menulist
for i in range(len(menulist)):
print ('%-4d%-20s%-15s%-15s' %(i + 1, menulist[i][0], menulist[i][2], menulist[i][1]))
list()
show_list()
for example, if the variable file is not set before the list() is called, the function needs to catch an error and prints an appropriate comment
You are using a built-in function name as your function name. Which is not considered as a good practice in Python. It replaces the built-in function list() which is used to create a list. And you need to define a variable before you can use it.
Here's how you can catch the error and print an appropriate comment, with the variable defined:
import csv
filep = str(input("Enter the file name with path : ")) # filepath
menulist = []
def calorieList():
"""Function to read the csv file, create a nested list
and return the list that is sorted based on calories
in the ascending order."""
global menulist
menulist = [] # store items
with open(filep) as csv_file: # read file
reader = csv.reader(csv_file, delimiter=",")
next(reader, None)
for row in reader:
row[2] = int(row[2].strip())
row[1] = float(row[1].strip())
if row[2] > 100 and row[2] < 200:
menulist.append(row)
menulist.sort(key=lambda x: x[-1])
def show_list(): # Function to display menu
global menulist
for i in range(len(menulist)):
print("%-4d%-20s%-15s%-15s" % (i + 1, menulist[i][0], menulist[i][2], menulist[i][1]))
try:
calorieList()
except FileNotFoundError:
print("Enter a valid file path.")
show_list()
I assume you meant filep by the file variable.
If you try to access filep before it is set, your program should raise NameError: name 'filep' is not defined.
But if you want to raise a custom error message, you can just use a try-except block as follows:
import csv
filep #filepath
menulist = []
def list():
"""Function to read the csv file, create a nested list
and return the list that is sorted based on calories
in the ascending order."""
global menulist
menulist = [] #store items
try:
with open(filep) as csv_file: #read file
reader = csv.reader (csv_file, delimiter=',')
next(reader, None)
for row in reader:
row[2] = int(row[2].strip())
row[1] = float(row[1].strip())
if row[2]> 100 and row[2] <200:
menulist.append(row)
except NameError:
raise ValueError("Your custom message here")
menulist.sort(key=lambda x: x[-1])
def show_list():#Function to display menu
global menulist
for i in range(len(menulist)):
print ('%-4d%-20s%-15s%-15s' %(i + 1, menulist[i][0], menulist[i][2], menulist[i][]))
list()
show_list()

Displaying sqlite3 data into Treeview

I am writing some code that shows a list of food with their information.
My code:
-connects to data base and gets items successfully
-loops correctly
Problem:
-If the name of my product is one word (ex:eggs) my code displays everything in the correct column
-If the name of my product is two or more words (ex:frosted flakes) my code displays 'frosted' on first column then 'flakes' in next columns which is incorrect
from tkinter import ttk
import tkinter as tk
import sqlite3
try:
from Tkinter import *
except ImportError:
from tkinter import *
def View():
db = sqlite3.connect("food_data.db")
cursor = db.cursor()
cursor.execute("SELECT name, quantity, expdate FROM food ORDER BY expdate ASC")
for row in cursor:
disp=('{0} {1} {2}'.format(row[0], row[1], row[2]))
tree.insert("",tk.END, values=disp)
db.close()
root = tk.Tk()
root.geometry("800x480")
tree = ttk.Treeview(column=("column1","column2","column3"),show='headings')
tree.heading("#1", text="Name")
tree.heading("#2", text="Quantity")
tree.heading("#3", text="Expiration Date")
tree.pack()
b2 = tk.Button(text="view data", command=View)
b2.pack()
root.mainloop()
It is suppose to successfully display items with multiple words in their name onto one column and not carry to the next one.
def View():
db = sqlite3.connect("food_data.db")
cursor = db.cursor()
cursor.execute("SELECT name, quantity, expdate FROM food ORDER BY expdate ASC")
for row in cursor:
# disp=('{0} {1} {2}'.format(row[0], row[1], row[2]))
tree.insert("",tk.END, values=(row[0], row[1], row[2]))
db.close()
Do it this way rather you have to insert the content in the treeview as tuple or list after iterating over it.
tree.insert("",tk.END, values=(row[0], row[1], row[2]))

AttributeError: 'WebElement' object has no attribute 'extract_first'

I am trying to run the script below to extract the tags from a webpage and save them into a csv file.
In details, I want to extract the tags associated to a class name.
However, I come across this error: AttributeError:
'WebElement' object has no attribute 'extract_first'.
The script is the following:
import csv
from selenium import webdriver
from time import sleep
from parsel import Selector
from selenium.webdriver.common.keys import Keys
from collections import defaultdict
from selenium.webdriver.support.select import Select
####### reading from the input file ##########
columns = defaultdict(list) # each value in each column is appended to a list
# get the list of keywords from the csv file
with open('query.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile) # read rows into a dictionary format
for row in reader: # read a row as {column1: value1, column2: value2,...}
for (k, v) in row.items(): # go over each column name and value
columns[k].append(v) # append the value into the appropriate list
# the list containing all of the keywords
search_query_list = columns['Keyword']
########## start scraping ###############
rb_results = []
# create a driver and let it open google chrome
driver = webdriver.Chrome("chromedriver")
# get website
driver.get('https://www.redbubble.com/')
sleep(0.5)
for i in range(len(search_query_list)):
next_query = search_query_list[i]
# get RB website
driver.get('https://www.redbubble.com/')
# get the search by its id
search_bar = driver.find_element_by_name("query")
sleep(0.5)
# enter the query to the search bar
search_bar.send_keys(next_query)
# press enter
search_bar.send_keys(Keys.RETURN)
sleep(1)
# from parsel's selector get the page source
sel1 = Selector(text=driver.page_source)
sleep(0.5)
# get first shirt //
continue_link = driver.find_element_by_class_name('shared-components-ShopSearchSkeleton-ShopSearchSkeleton__composedComponentWrapper--1s_CI').click()
sleep(1)
sel2 = Selector(text=driver.page_source)
sleep(0.5)
################## get TAGS ###############
# Check tags for all products
try:
# get the tags for the search query
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").extract_first()
tags_rb = str(tags_rb)
# if number of products is found print it and search for the prime
# print the number of products found
if tags_rb == None:
rb_results.append("0")
else:
#rb_results = str(tags_rb)
rb_results.append(tags_rb)
except ValueError:
pass
#except:
#rb_results.append("errore")
###### writing part ########
with open ("rb_results.csv","w", newline='') as resultFile:
writer = csv.DictWriter(resultFile, fieldnames=["Rb Results"],delimiter='\t')
writer.writeheader()
writer.writerows({'Rb Results': item} for item in rb_results)
resultFile.close()
Any ideas about how to fix it and extract the text of shared-components-Tags-Tags__listContent--oLdDf ? Many thanks!!!
If I right understand, you want an element text. So you can do it like this:
replace:
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").extract_first()
with:
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").text
You are getting this error:
'WebElement' object has no attribute 'extract_first'.
because WebElement does not have method .extract_first().
PS: you don't need this:
tags_rb = str(tags_rb)
The code block to replace is:
# Check tags for all products
try:
# get the tags for the search query
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").text # get text
# tags_rb = str(tags_rb) no need in this line
# if number of products is found print it and search for the prime
# print the number of products found
if tags_rb == None:
rb_results.append("0")
else:
#rb_results = str(tags_rb)
rb_results.append(tags_rb)
except ValueError:
pass

Searching from csv file in python using re.match()

I am trying to put a module in my program that is able to get a string inputted in an entry box and search it in an existing csv file within the same directory. after that, to see if the string has been found, it should either print found or not found. its currently showing the error:
TypeError: 'type' object is not subscriptable
from tkinter import *
from tkinter import ttk
import csv
import re
import os
win= Tk()
win.resizable(0, 0)
win.title('PRODUCT QUERY')
text_input=StringVar()
int_input1=IntVar()
int_input1.set('')
int_input2=IntVar()
int_input2.set('')
def update():
import product_updater
def searcher():
with open('products_database.csv', 'r') as x:
global word
word=str[int_input1]
y=x.readlines()
dbase_list=list(y)
for i in word:
if re.search(i, dbase_list):
print('found')
else:
print('not found')
a=Label(win, text='Scan barcode').grid(column=0, row=0)
b=Entry(win, text=int_input1).grid(column=1, row=0)
c=Label(win, text='Item').grid(column=0, row=1)
d=Entry(win, text=text_input).grid(column=1, row=1)
e=Label(win, text='Sale price').grid(column=0, row=2)
f=Entry(win, text=int_input2).grid(column=1, row=2)
g=Button(win, text='Verify', command=searcher, width=20).grid(column=0, row=3, columnspan=1)
h=Button(win, text='Add product', command=update, width=20).grid(column=1, row=3)
win.mainloop()
As Andre suggested, pandas is a great library for .csv manipulations.
In this case, to find if a string is included within the table, you can do:
import pandas as pd
def find_string_csv(file_name, to_find):
my_csv = pd.read_csv(file_name)
for col in my_csv:
if my_csv[col].str.contains(to_find).any():
print('found it')
break
else:
print('not found')
The .contains method runs a regex matching option by default. This code will iterate over each columns and check if the string is present in any of them. If it is, it will break after the first occurrence and print found it.
Note that it will not match the headers of the columns, so if you have a column named 'abc', running this function with to_find == 'abc', it will not find it. If you want to include the column names in the query, add header=None in your read_csv() parameters.
Additionally, if you prefer one liners, you can replace the whole for / else loop with:
print('found it') if my_csv.isin([to_find]).any().any() else print('not found')
The isin will return a new pd.Dataframe with boolean values of True if equals to to_find and False otherwise. Calling any on it will give you a pd.Serie where each entry will be a boolean representing whether a given column has a True inside. The second any will give you a boolean of True if any column is True, False otherwise.
I find the for loop clearer and easier to read though.

initialising ttk.Entry in dictionary

Basically I have 8 entries I want to create in a loop and store in a dictionary, with a string as key:
class foo:
entries={}
keys=[#some keys are here, type string]
#do other stuff
def create_entries(self):
for key in keys:
entries[key]=ttk.Entry(self.frame,text='sometext')
#other stuff going on
def assign(self):
f=open('file.name','r').read()
#do some fancy slicing to get the strings for the entries
for key in keys:
entries[key].insert(0,string)
now here it fails, stating that 'NoneType' object has no attribute 'insert'.
I guess this is because I have declared entries as an empty dictionary.
But if I declare it like this: entries={'KEY':ttk.Entry} still states there is no insert for 'NoneType'. And if I declare it like entries={'KEY':ttk,Entry()} it initalises an empty toplayer on start, but if I come to load my entries, it tells me again, there is no insert for 'NoneType'.
So I am kind of lost right now.. is it even possible to initialise entries into a dictionary and later on insert some text in them? Or do I have to stick with each entry as a "individual variable"?
minimum working example:
If I delete the prints in the read-function and uncomment the inserts, it tells me:
self.entries[key].insert(0,s)
AttributeError: 'NoneType' object has no attribute 'insert'
import tkinter as tk
from tkinter import ttk
f = open('testfile.test', 'w')
f.write('A=non-relevant\nB=SomeFunnyStuff\nZ=MySecretCode:9128\n')
f.close()
class foo:
main = tk.Tk
frame = ttk.LabelFrame
keys = ['A','B','Z']
entries={}
labels={}
def read(self):
f = open('testfile.test','r').read()
for key in self.keys:
first=key+'='
if key != 'Z':
i = self.keys.index(key)+1
last='\n'+self.keys[i]+'='
s=f.split(first)[1].split(last)[0]
print(s)#self.entries[key].insert(0,s)
else:
s=f.split(first)[1]
print(s)#self.entries[key].insert(0,s)
def _quit(self):
self.main.quit()
self.main.destroy()
exit()
def run(self):
self.main.mainloop()
def __init__(self):
#init window
self.main = tk.Tk()
self.main.title('Test')
self.frame = ttk.LabelFrame(self.main, text='Try it').grid(column=0,row=0)
#init entries & labels
c,r=0,0
for key in self.keys:
s = key+'='
self.labels[key] = ttk.Label(self.frame, text=s).grid(column=c,row=r)
c+=1
self.entries[key] = ttk.Entry(self.frame,text=s).grid(column=c,row=r)
r+=1
c-=1
self.button = ttk.Button(self.frame, text='close',command=lambda:self._quit()).grid(column=0,row=r)
self.read()
print(self.entries.values())
t = foo()
t.run()
If anyone has a similar problem:
according to this post the problem is with how I initialized the entries in my original code & the minimum example posted (not in the original post unfortunately..):
self.entries[key] = ttk.Entry(self.frame,text=s).grid(column=c,row=r) returns a None to the dictionary, as .grid() returns None
Initializing labels and entries first and then using grid in a separate line however works fine:
self.entries[key] = ttk.Entry(self.frame,text=s)
self.entries[key].grid(column=c,row=r)
That fixed it for me.

Resources