So, first of all, here is the code that I'm working on:
# import required modules
from tkinter import *
import pandas as pd
import os
import datetime
# Create an instance of tkinter frame
window = Tk()
# Set the size of the tkinter window
window.geometry("1920x1080")
# Add Textbox widget for folder name
folder_name = Text(window, width=20, height=1, font=('Arial', 10))
folder_name.grid(row=2,column=7, sticky="ew")
folder_name.insert(INSERT, "Número NF")
df = None
all_data = []
current_row = 0
copied_row_widgets = []
cells = {}
column_names = []
def clear_widgets():
global current_row
global all_data
global copied_row_widgets
global cells
for widget in copied_row_widgets:
widget.destroy()
copied_row_widgets.clear()
all_data.clear()
#cells.destroy()
current_row = current_row
def refresh():
clear_widgets()
global current_row
global df
#global column_names
# Retrieve folder name from Textbox widget
folder_name_text = folder_name.get("1.0", "end-1c").strip()
# Create folder with folder name
base_path = "C:\\Users\\rrios\\Documents\\"
folder_path = os.path.join(base_path, folder_name_text)
if not os.path.exists(folder_path):
os.mkdir(folder_path)
# Clear the all_data list
all_data.clear()
# Load data from source
df = pd.read_excel(os.path.join(folder_path, folder_name_text + '.xlsx'), header=0)
df = df.fillna('')
df = df.astype(str)
df = df.replace(to_replace = "\.0+$",value = "", regex = True)
pd.set_option('display.max_colwidth', None)
# Extract number of rows and columns
n_rows = df.shape[0]
n_cols = df.shape[1]
# Initialize current_row with the total number of rows in the dataframe
current_row = n_rows
# Extracting columns from the data and creating text widget with some background color
column_names = df.columns
i=0
for j, col in enumerate(column_names):
text = Text(window, width=20, height=1, font=('Arial', 10,'bold'), bg = "#9BC2E6")
text.grid(row=i,column=j, sticky="ew")
text.tag_configure("tag_name", justify='center')
text.insert(INSERT, col)
text.tag_add("tag_name", "1.0", "end")
# Dictionary for storing the text widget references
# cells = {}
# adding all the other rows into the grid
for i in range(n_rows):
for j in range(n_cols):
text = Text(window, width=len(str(df.loc[i][j])), height=1, font=('Times', 8))
text.grid(row=i+1,column=j, sticky="ew")
text.tag_configure("tag_name", justify='center')
text.insert(INSERT, df.loc[i][j])
text.tag_add("tag_name", "1.0", "end")
cells[(i,j)] = text
# create button widget and add it to the grid
button = Button(window, text="Copy Row", command=lambda row=i: copy_row(cells, n_cols, row))
button.grid(row=i+1,column=n_cols)
def copy_row(cells, n_cols, row):
global current_row
global all_data
#global column_names
# Get the data in the current row
data = [cells[(row, j)].get("1.0", "end-1c").strip() for j in range(n_cols)]
if current_row < n_rows:
# Update the current row in the GUI with the copied values
for j in range(n_cols):
text = cells[(current_row,j)]
text.configure(width=len(str(data[j])))
text.delete("1.0", END)
text.insert(INSERT, data[j])
text.tag_add("tag_name", "1.0", "end")
else:
# Add new row to the GUI with the copied values
for j in range(n_cols):
text = Text(window, width=len(str(data[j])), height=1, font=('Times', 8))
text.grid(row=current_row+n_rows,column=j, sticky="ew")
text.tag_configure("tag_name", justify='center')
text.insert(INSERT, data[j])
text.tag_add("tag_name", "1.0", "end")
cells[(current_row+n_rows,j)] = text
all_data.append(data)
copied_row_widgets.append(text)
current_row += 1
def do_something():
global df
global all_data
#global column_names
# Add new rows to the DataFrame
new_data = pd.DataFrame(all_data, columns=column_names)
df = pd.concat([df, new_data], ignore_index=True)
for i in range(n_rows):
for j in range(n_cols):
if df.loc[i][j] != cells[(i,j)].get("1.0", "end-1c"):
df.loc[[i],column_names[j]] = cells[(i,j)].get("1.0", "end-1c").strip()
print(df)
df['fab'] = df['fab'].apply(lambda x: datetime.datetime.strptime(x, '%d%m%Y').strftime('%Y/%m/%d') if x and len(x) != 10 else x)
df['val'] = df['val'].apply(lambda x: datetime.datetime.strptime(x, '%d%m%Y').strftime('%Y/%m/%d') if x and len(x) != 10 else x)
print(df)
# Save xlsx file in the created folder with the folder name as the file name
df.to_excel(os.path.join(folder_path, folder_name_text + '.xlsx'), index=False)
save_button = Button(
window, height = 1,
width = 16,
text ="salvar",
bg = "#1ad03f",
command = lambda:do_something())
save_button.grid(row=0,column = 8)
clear_button = Button(
window, height = 1,
width = 16,
text ="limpar",
bg = "#3b77a8",
command = lambda:clear_widgets())
clear_button.grid(row=0,column =10)
refresh_button = Button(
window, height = 1,
width = 16,
text ="atualizar",
bg = "#3b77a8",
command = lambda:refresh())
refresh_button.grid(row=0,column = 7)
window.mainloop()
I'm having a problem that is, when I copy a row with the button copy row, on the GUI it works perfectly, it shows only 1 row copied, everything works flawlessly.
But when I save, it saves lots of copies from the row that I copied, 6 or more. And I can't figure out what's happening, could someone shine a light for me?
to reproduce:
It loads an xlsx file as database, the input is for the name of file and folder, for example folder 123 containing file 123.xlsx (you can modify the base path to the path that you put this file + folder) the excel file should have 6 columns: N° do item, descricao do item, quant, lot, fab, val put any number on N° do item, any text on descricao do item, any number on quant, any number on lot, any date on fab and val.
On the input you put the file/folder name then click atualizar the program will load and show the database displayed on text boxes filled with data from the excel file, and you will have a button to copy row, click it then click salvar.
after that, click atualizar so It reloads the xlsx you just saved. it should show the multiple entries generated by copying just 1 row.
Related
I am trying to display the data from a file and a button and then once the button is clicked, display the new data from a new file along with a button. You can see my print statements in my attempt to debug this. When I run the program, there is output, and it correctly displays a # and a button. However, the # that is displayed is from the last file I have (file #3) instead of from file #1. I believe that file #1 was covered by file #2 which then got covered by file #3. All of this happened without any button being clicked. How can I make the program wait until the button is clicked before displaying the new # and button?
window = Tk()
def clicked():
top = Toplevel(window)
top.geometry('300x300')
popLabel = Label(top, text = "E")
popLabel.place(relx = 0.5, rely = 0.5, anchor = 'center')
for widgets in frame1.winfo_children():
widgets.destroy()
for x in range(1,4):
fileName = "file" + str(x) + ".json"
print(fileName)
frame1 = LabelFrame(window, width = 300, height = 300, padx=10,pady=5)
frame1.grid(row= 0,column=0)
with open(fileName) as f:
data = json.load(f)
#print(data)
num = "#" + data.get("id")
print(num)
numLabel = Label(
frame1,
text = num
).grid(row = 1, column = 1)
firstButton = Button(
frame1,
text = "A",
command = clicked
).grid(row = 2, column = 1, sticky = 's')
window.mainloop()
I guess your real problem was that you overwrite the frame every time in the loop. So define your frame before the loop and set the column number as a variable.
import tkinter as tk
window = tk.Tk()
def clicked(number):
top = tk.Toplevel(window)
top.geometry('300x300')
fileName = "file" + str(number)+ ".json"
# with open(fileName) as f:
# data = json.load(f)
data = "data"
num = "#" # data.get("id")
popLabel = tk.Label(top, text = fileName)
popLabel.place(relx = 0.5, rely = 0.5, anchor = 'center')
#for widgets in frame1.winfo_children():
# widgets.destroy()
frame1 = tk.LabelFrame(window, width = 300, height = 300, padx=10,pady=5)
frame1.place(relwidth = 1, relheight= 1)
for x in range(1,4):
num = "#"
numLabel = tk.Label(
frame1,
text = num
).grid(row = 1, column = x)
firstButton = tk.Button(
frame1,
text = "A_{}".format(x),
command= lambda x =x: clicked(x)
).grid(row = 2, column = x, sticky = 's')
window.mainloop()
EDIT:
In this case, I would put the open command in the checked function and tell them the number of parameters to load.
im trying to name a button what it is named in ent and so it doesn't repeat when the button is pressed again so if you press once it's button1 and again button2
from tkinter import *
def ext():
win1.destroy()
but1 = Button(root, text=txt.get(), height=10, width=30)
but1.grid(padx=3, row=0, column=1)
def create():
global win1
global txt
win1 = Tk()
win1.geometry("200x200")
ent = Entry(win1)
ent.pack(pady=20)
txt = ent.get()
sub = Button(win1, text="Submit", command=ext)
sub.pack()
root = Tk()
root.geometry("750x750")
root.config(background="#6673ED")
create_but = Button(root, text="Create new card", height=10, width=30, command=create)
create_but.grid(row=0,column=0)
root.mainloop()
The code below uses a dictionary of lists to add an integer to any repeating text input. I think this is what the question is about.
import tkinter as tk # import as tk is safer and more flexible
# Globals to keep it simple
names = {} # Dictionary. Will become a dictionary of lists.
row = 1
col = 0
win1 = None
ent = None
def add_text( txt ):
""" Adds txt to the names dictionary if it doesn't already exist.
Adds and integer to the txt if it does already exit """
name = names.get( txt, None )
if name:
name.append( txt + str(len( name )) ) # Append `txt + int` to a list
else:
names[ txt ] = [ txt ] # Add a list of one item to the dict.
# print( names ) # Uncomment to see what is happening.
return names[ txt ][-1]
def ext():
global row, col
txt = ent.get() # Get the text from the entry
win1.destroy() # before the window is destroyed
txt = add_text( txt )
but1 = tk.Button(root, text=txt, height=10, width=30)
but1.grid(padx=3, row=row, column=col) # row and column need to
# change to show all buttons.
col += 1
if col > 2:
row += 1
col = 0
def create():
global win1
global ent
win1 = tk.Toplevel() # Create a second window with tk.Toplevel.
# Never create two tk.Tk objects.
win1.geometry("200x200")
ent = tk.Entry(win1)
ent.pack(pady=20)
ent.focus() # Position the focus in the Entry
# txt = ent.get() # removed as the Entry was being read before data was entered.
# The entry is now read in `ext`.
sub = tk.Button( win1, text="Submit", command=ext )
sub.pack()
root = tk.Tk()
root.geometry("750x750")
root.config(background="#6673ED")
create_but = tk.Button(root, text="Create new card", height=10, width=30, command=create)
create_but.grid(row=0,column=0)
root.mainloop()
I'm a newbie at coding in general. I'm trying to create a program that downloads the first 20 google images and puts them on buttons using tkinter. I've managed to create the grid and the buttons. If I put the 'create_grid' code outside of the function, the last image appears, but not the rest of them. I assume it has something to do with garbage collection, but I've been banging my head with this all week. Any idea on how it make it work would be more than appreciated.
from tkinter import *
from PIL import ImageTk,Image
from icrawler.builtin import GoogleImageCrawler
import time
import os
fileLocation = "C:\\Python projects\\tkinter\\images\\"
fileList = os.listdir(fileLocation)
root = Tk()
root.title("Product image search")
root.iconbitmap("favicon.ico")
e = Entry(root, borderwidth=5)
e.grid(row=0, column=0, columnspan=5)
length = len(fileList)
i = 0
rows = 0
cols = 1
button_list = []
def create_grid():
global i, rows, cols
while i < length:
# Define image boxes
img_path = (f"{fileLocation}{fileList[i]}")
img_raw= Image.open(img_path)
print(img_path)
img_resize = img_raw.resize((200, 200), Image.ANTIALIAS)
img_1 = ImageTk.PhotoImage(img_resize)
img_btn = Button(image=img_1)
img_btn.grid(row= rows, column= cols)
button_list.append(img_btn)
rows += 1
if rows == 5:
rows = 0
cols += 1
i += 1
def crawlerAction():
google_crawler = GoogleImageCrawler(
parser_threads=2,
downloader_threads=4,
storage={'root_dir': 'images'})
google_crawler.crawl(keyword=(e.get()), max_num=20)
time.sleep(5)
create_grid()
search_btn = Button(text="検査", command=crawlerAction)
search_btn.grid(row=0, column=3)
root.mainloop()
You need to call os.listdir inside the function. If you call it before, your images haven't been downloaded yet, so the folder is empty. Moving fileLocation, fileList and length inside the function should fix it.
root = Tk()
root.title("Product image search")
root.iconbitmap("favicon.ico")
e = Entry(root, borderwidth=5)
e.grid(row=0, column=0, columnspan=5)
i = 0
rows = 0
cols = 1
button_list = []
def create_grid():
global i, rows, cols
fileLocation = "C:\\Python projects\\tkinter\\images\\"
fileList = os.listdir(fileLocation)
length = len(fileList)
while i < length:
# Define image boxes
img_path = (f"{fileLocation}{fileList[i]}")
img_raw= Image.open(img_path)
print(img_path)
img_resize = img_raw.resize((200, 200), Image.ANTIALIAS)
img_1 = ImageTk.PhotoImage(img_resize)
img_btn = Button(image=img_1)
img_btn.grid(row= rows, column= cols)
button_list.append(img_btn)
rows += 1
if rows == 5:
rows = 0
cols += 1
i += 1
If your images still do not show up, they may be getting garbage collected. In that case, add img_btn.image = img_1 on the line after img_btn = Button(.... By creating a reference to the PhotoImage object, it won't get garbage collected.
I am running into an error with my MultiColumnListbox. I am trying to understand how to fix this issue. I am trying to see what is selected in my multicolumn listbox. The mutlicolumn listbox is built using tkinter and has a submit button which should run a function telling me what rows were selected in the multi column listbox. I am receiving this error 'MultiColumnListbox' object has no attribute 'curselection' and cant seem to fix it.
This is my code
import os
import time
import glob
import datetime
from array import *
try:
import Tkinter as tk
import tkFont
import ttk
except ImportError: # Python 3
import tkinter as tk
import tkinter.font as tkFont
import tkinter.ttk as ttk
class MultiColumnListbox(object):
"""use a ttk.TreeView as a multicolumn ListBox"""
def submitFunction():
selection = listbox.curselection()
for k in range(0,len(selection)):
selected = listbox.curselection()[k]
def __init__(self):
self.tree = None
self._setup_widgets()
self._build_tree()
def _setup_widgets(self):
s = """\click on header to sort by that column
to change width of column drag boundary
"""
msg = ttk.Label(wraplength="4i", justify="left", anchor="n",
padding=(10, 2, 10, 6), text=s)
msg.pack(fill='x')
container = ttk.Frame()
container.pack(fill='both', expand=True)
# create a treeview with dual scrollbars
self.tree = ttk.Treeview(columns=car_header, show="headings")
vsb = ttk.Scrollbar(orient="vertical",
command=self.tree.yview)
hsb = ttk.Scrollbar(orient="horizontal",
command=self.tree.xview)
self.tree.configure(yscrollcommand=vsb.set,
xscrollcommand=hsb.set)
self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
vsb.grid(column=1, row=0, sticky='ns', in_=container)
hsb.grid(column=0, row=1, sticky='ew', in_=container)
container.grid_columnconfigure(0, weight=1)
container.grid_rowconfigure(0, weight=1)
def _build_tree(self):
for col in car_header:
self.tree.heading(col, text=col.title(),
command=lambda c=col: sortby(self.tree, c, 0))
# adjust the column's width to the header string
self.tree.column(col,
width=tkFont.Font().measure(col.title()))
for item in car_list:
self.tree.insert('', 'end', values=item)
# adjust column's width if necessary to fit each value
for ix, val in enumerate(item):
col_w = tkFont.Font().measure(val)
if self.tree.column(car_header[ix],width=None)<col_w:
self.tree.column(car_header[ix], width=col_w)
def sortby(tree, col, descending):
"""sort tree contents when a column header is clicked on"""
# grab values to sort
data = [(tree.set(child, col), child) \
for child in tree.get_children('')]
# if the data to be sorted is numeric change to float
#data = change_numeric(data)
# now sort the data in place
data.sort(reverse=descending)
for ix, item in enumerate(data):
tree.move(item[1], '', ix)
# switch the heading so it will sort in the opposite direction
tree.heading(col, command=lambda col=col: sortby(tree, col, \
int(not descending)))
FilesToDeleteName = [];
FilesToDeletePath = [];
FilesToDeleteDate = [];
car_list = [];
car_list.append([])
car_list.append([])
#str1 = input("Enter number of days old: ")
#days = int(str1)
days = 90
count = 0
time_in_secs = time.time() - (days * 24 * 60 * 60)
extf = ['Windows','Program Files', 'Program Files (x86)']
for (root, dirs, files) in os.walk('C:/Users', topdown=True):
dirs[:] = [d for d in dirs if d not in extf]
for filename in files:
Fullname = os.path.join(root,filename)
stat = os.stat(Fullname)
modified = datetime.datetime.fromtimestamp(stat.st_mtime)
if Fullname.endswith('.pcapng') or Fullname.endswith('.evtx') or Fullname.endswith('.png') or Fullname.endswith('.sql') or Fullname.endswith('.etl') or Fullname.endswith('.zip'):
if stat.st_mtime <= time_in_secs:
FilesToDeleteName.append(filename);
FilesToDeletePath.append(Fullname);
FilesToDeleteDate.append(str(modified));
FilesToDelete = [];
for p in range(0,len(FilesToDeletePath)):
STR2 = FilesToDeletePath[p],FilesToDeleteDate[p]
FilesToDelete.append(STR2)
car_list = FilesToDelete
car_header = ["Path ", "Date Modified"]
if __name__ == '__main__':
window = tk.Tk()
window.title("Multicolumn Treeview/Listbox")
listbox = MultiColumnListbox()
def submitFunction():
print(listbox.curselection(self))
window.destroy()
def closeFunction():
window.destroy()
submit = tk.Button(window, text='Submit', command=submitFunction)
submit.pack(side=tk.RIGHT, padx = 20)
close = tk.Button(window, text='Close', command=closeFunction)
close.pack(side=tk.RIGHT)
window.mainloop()
This is the main part of my issue
def submitFunction():
print(listbox.curselection(self))
window.destroy()
I will ultimately be trying to get the index numbers to delete the given file path
This question already has answers here:
Tkinter: AttributeError: NoneType object has no attribute <attribute name>
(4 answers)
Closed 3 years ago.
The code I have attached below functions like this: It takes users ID and marks. It goes through an excel sheet and finds that specific ID. Then it will update the second column in that row with his marks.
However, this code gives me an error when I run it. The error comes from .get() function used for getting value of an Entry. I have used .get() function in other projects where it works.
import xlwt
import xlrd
from xlutils.copy import copy
from tkinter import *
root = Tk()
root.title("Quiz marks uploader")
root.geometry("500x500")
rnn = "global"
maar = "global"
def upload():
rn = entry_1.get()
mar = entry_2.get()
rnn = rn
maar = mar
button_1 = Button(root, text = "Upload", command = upload).place(relx = 0.3,rely = 0.2,anchor = NE)
label_1 = Label(root, text = "Enter Reg No here").place(relx = 0.2,rely = 0.05,anchor = E)
entry_1 = Entry(root).place(relx = 0.5,rely = 0.05,anchor = E)
label_2 = Label(root, text = "Enter marks here").place(relx = 0.2,rely = 0.1,anchor = E)
entry_2 = Entry(root).place(relx = 0.5,rely = 0.1,anchor = E)
workbook = xlrd.open_workbook("file.xls")
sheet = workbook.sheet_by_index(0)
rb = xlrd.open_workbook("file.xls")
wb = copy(rb)
w_sheet = wb.get_sheet(0)
for row in range(sheet.nrows):
row_value = sheet.row_values(row)
if row_value[0] == rnn:
w_sheet.write(row,1,maar)
print (row_value)
wb.save("file.xls")
root.mainloop()
This is where you are going wrong:
You are doing:
entry_1 = Entry(root).place(relx = 0.5,rely = 0.05,anchor = E)
In this case, the entry_1 is a NoneType object.
And NoneType object doesn't have any property get(), therefore it throws an error.
What you should do:
First instantiate an Entry widget and then give your widget a position in the window using the widget reference.
entry_1 = Entry(root)
entry_1.place(relx = 0.5,rely = 0.05,anchor = E)
Updated code:
import xlwt
import xlrd
from xlutils.copy import copy
from tkinter import *
root = Tk()
root.title("Quiz marks uploader")
root.geometry("500x500")
rnn = "global"
maar = "global"
def upload():
rn = entry_1.get()
mar = entry_2.get()
rnn = rn
maar = mar
button_1 = Button(root, text = "Upload", command = upload).place(relx = 0.3,rely = 0.2,anchor = NE)
label_1 = Label(root, text = "Enter Reg No here").place(relx = 0.2,rely = 0.05,anchor = E)
# entry widget 1
entry_1 = Entry(root)
entry_1.place(relx = 0.5,rely = 0.05,anchor = E)
label_2 = Label(root, text = "Enter marks here").place(relx = 0.2,rely = 0.1,anchor = E)
# entry widget 2
entry_2 = Entry(root)
entry_2.place(relx = 0.5,rely = 0.1,anchor = E)
workbook = xlrd.open_workbook("file.xls")
sheet = workbook.sheet_by_index(0)
rb = xlrd.open_workbook("file.xls")
wb = copy(rb)
w_sheet = wb.get_sheet(0)
for row in range(sheet.nrows):
row_value = sheet.row_values(row)
if row_value[0] == rnn:
w_sheet.write(row,1,maar)
print (row_value)
wb.save("file.xls")
root.mainloop()
Hope it helps!
Instead of xlrd use pandas for managing excel files.
To create simple excel file:
import pandas as pd
df = (pd.DataFrame({'ID': [10, 11, 12], 'Mark': ['ww','zz','cc']}))
df.to_excel('data.xlsx')
I add some changes in elements positions.
Now you can update data like below:
import pandas as pd
from tkinter import *
root = Tk()
root.title("Quiz marks uploader")
root.geometry("500x500")
def upload():
id = entry_id.get()
mark = entry_mark.get()
# load excel into data frame
df = pd.read_excel('data.xlsx', index_col=0)
# find proper row and replace value
row_match = df[df['ID'] == int(id)]
if row_match.shape[0] > 0:
df.at[row_match.index[0], 'Mark'] = mark
# save updated file
df.to_excel('data.xlsx')
# set button and labels
button = Button(root, text = "Upload", command=upload)
button.grid(column=1, row=4)
label = Label(root, text = "Id")
label.grid(column=0, row=2)
entry_id = Entry(root)
entry_id.grid(column=1, row=2)
label = Label(root, text = "Mark")
label.grid(column=0, row=3)
entry_mark = Entry(root)
entry_mark.grid(column=1, row=3)
root.mainloop()