How to make border for my table using python tkinter? - python-3.x

Apparently, my application can display the excel file but it is a bit messy without border for the table.
import pandas as pd
import xlrd
import tkinter as tk
from tkinter import*
from tkinter import ttk, filedialog
root = tk.Tk()
root.title("My Application")
width = 1000
height = 500
def browseFile():
global workbook, copyWorkbook, excel_file, sheetName, worksheet, df_table
fileName = filedialog.askopenfilename(initialdir = '/', title = 'New File', filetypes = (('excel file', '.xlsx'), ('excel file', '.xls'), ('all files', '*.*')))
excel_file = pd.ExcelFile(fileName)
workbook = xlrd.open_workbook(fileName)
sheetCount = workbook.nsheets
sheetName = []
tab = []
for x in range(workbook.nsheets):
tab.append(ttk.Frame(tabControl))
sheetName = workbook.sheet_names()
tabControl.add(tab[x], text = sheetName[x])
df_table = excel_file.parse(sheetName[x])
lblTable = Label(tab[x], text = df_table.to_string(index = False)).pack()
toolbar = Frame(root)
btnOpen = Button(toolbar, text = "Open", command = browseFile).pack(side = LEFT)
btnQuit = Button(toolbar, text = "Quit", command = root.quit).pack(side = RIGHT)
toolbar.pack(side = TOP, fill = X)
tabControl = ttk.Notebook(root)
tabHome = ttk.Frame(tabControl)
tabControl.pack(expand = 1, fill = 'both', side = LEFT)
root.mainloop()
I have tried search statements that can display the table with border, but no result found. How can I add border to the table? Is it possible to add border? If not, what other method that I can use?

The problem
Your problem here is that, your code puts the entire dataframe into a single Label widget. When I tried putting a border around this label, it appeared around the whole dataframe, rather than around each cell.
My solution
My solution to this is to go through all of the rows and create an individual Label widget for each cell, giving each of these its own border. I then organised them using .grid() rather than .pack(). For the border, I used the settings borderwidth=2, relief="ridge", but you can choose others.
I also added a feature that sets the width of each column to its longest value to prevent the label's contents overflowing.
I did not include anything to include row and column headers, but you replacing i.pop(0) with i[0] and changing header=none should add this feature.
Modified code
I have only included the for loop in your browseFile() function as I have not made any changes to the rest of your code.
for x in range(workbook.nsheets):
tab.append(ttk.Frame(tabControl))
sheetName = workbook.sheet_names()
tabControl.add(tab[x], text = sheetName[x])
df_table = excel_file.parse(sheetName[x], header=None) # header=None stops the first line of the data table being used as the column header, making it appear in the data table
# Iterates through each row, creating a Label widget for each cell on that row.
for i in df_table.itertuples():
i=list(i) # Converts the row to a more helpful list format
row = i.pop(0) # pop returns value at position, then removes it, as we don't wan't first value of tuple (row number) in spreadsheet
for j in range(len(i)):
# Next two lines get the current column in a list format and convert all items to a string, in order to determine the longest item to make all label widgets the correct width.
current_column = list(df_table.iloc[:, j])
for k in range(len(current_column)): current_column[k] = str(current_column[k])
# Makes label widget
lbl = Label(tab[x], text = i[j], borderwidth=2, relief="ridge", width=len(max(current_column, key=len)), justify=LEFT).grid(row=row, column=j+1)
PS: I really enjoyed solving this question. Thanks for asking!

Related

Get row data for Treeview from Panda

I have a .xlsx file and I want to recreate that table in a GUI using a Treeview from TKinter. I have a solution below that gives me the output I want but it's long and I'm not sure if there's a better way to do it. For my application, performance is a concern because I don't have that much power and I think with a larger .xlsx file I'll start to see a performance hit.
I also have to assume that I don't know the heading and number of rows but that the number of columns is less than 15
import numpy as np
import pandas as pd
xls = pd.ExcelFile('file.xlsx');
sheetData = pd.read_excel(xls, 'Sheet-1')
# Get column headings
headings = sheetData.columns
# Convert headings to list
data = list(headings.values.tolist())
# Get row count
rows = len(sheetData)
# Create tree with the the number of columns
# equal to the sheet, the id of the column
# equal to the column header and disable
# the 'treeview'
tree = ttk.Treeview(self, columns=data, show=["headings"],selectmode='browse')
# Create column headings on tree
for heading in headings:
heading = str(heading) # convert to string for processing
tree.column(heading, width=125, anchor='center')
tree.heading(heading, text=heading)
# Populate rows --The part that concerns me
for rownumber in range(rows):
rowvalue = sheetData.values[rownumber] # Get row data
rowvalue = np.array2string(rowvalue) # Convert from an np array to string
rowvalue = rowvalue.strip("[]") # Strip the string of square brackets
rowvalue = rowvalue.replace("'",'') # Replace all instances of ' with no character
tree.insert('', 'end', values= rowvalue) # Append the row to table
Is there a simpler way to get row data and append it to a treeview?
I create an easy example to do this:
import tkinter as tk
from tkinter import ttk
import pandas as pd
# Maybe This is what you want
def Start():
fp = pd.read_excel("./test.xlsx") # Read xlsx file
for _ in range(len(fp.index.values)): # use for loop to get values in each line, _ is the number of line.
tree.insert('','end',value=tuple(fp.iloc[_,[1,2]].values)) # [_,[1,2]] represents that you will get the values of second column and third column for each line.
win = tk.Tk()
win.wm_attributes('-topmost',1)
# win.geometry("+1300+0")
ttk.Button(win,text="Import it",command=Start).pack()
columns = ("name","gender")
tree = ttk.Treeview(win,show="headings",columns=columns)
tree.column("name",width=100,anchor='center')
tree.column("gender",width=50, anchor="center")
tree.heading("name",text="name")
tree.heading("gender",text="gender")
tree.pack()
win.mainloop()
This is my example excel file:
Result:

Python tkinter grid, row value being ignored in a ScrolledText Widget

I want to have a space between the Entry widget and the ScrollText Widget. I thought it would be as simple as setting row=2 for the grid. The strange thing is no matter what I set for row the ScrollText widget always starts at row 1. I suppose I could add an empty text widget to make the space but I am confused as to why setting the row is not working for me.
from tkinter import *
from tkinter import scrolledtext
main = Tk()
main.title("Grid Prob")
main.geometry('750x750')
text = StringVar()
entSearch = Entry(main, textvariable = text, width = 50, font='arial 12',
highlightthickness=1)
entSearch.grid(row = 0, column = 1)
txt = """This is some text to be entered into the text widget. It should
word wrap when the text exceeds the width of the widget."""
textw = scrolledtext.ScrolledText(main,width=70,height=33)
# No matter what number I put for row the scroll widget always starts in
# row 1
textw.grid(column=1, row=3)
textw.config(background="light grey", foreground="black",
font='arial 12', wrap='word')
textw.insert(END, txt)
main.mainloop()
If I interpreted what Bryan Oakley said in the comments above correctly the problem is that since my code didn't put anything into row 2 by default it (row 2) has a height of 0. This means that I am putting my ScrollText Widget into row 3 as intended but since row 2 has a height of 0 visually it looks like it's being placed in row 2. I added a blank label to my code with a height of one and placed it on row 2. This provided the space that I was looking for. Here is the additional code I added.
label_1 = Label(main, width = "70", height = "1")
label_1.grid(row=1, columnspan=3)
It seems weird that tkinter makes you add widgets just to provide a space. If anyone knows of a better way to do this please let me know.

How to use spinbox for selection of items in a list and then using add to cart button to add that item to the invoice?Python 3

from tkinter import *
These are two lists which has prices and product details.
price_audible = ['$1.99', '$1.50', '$1.00', '$1.99', '$1.50', '$1.00', '$1.99', '$1.50', '$1.00', '$1.99']
description_audible = ['1: Aaaaaa', '2: bbbbbb', '3: ccccccccc', '4: dddddddddd', '5:eeeeeeee',
'6: fffffff', '7: gggggggg', '8: hhhh', '9: ii', '10: jjjjjjjjjjjjjjjjj']
audible_newlist = ''
for m in range(0, 10):
'\n'
var_1=('#'+description_audible[m])+ '\n' +('('+price_audible[m]+')')+'\n'
audible_newlist = audible_newlist + var_1
show_window = audible_newlist
dash_1 = Tk()
dash_1.configure(bg = 'red')
dash_1.title("Amazon Bestsellers Audible")
show_window = StringVar()
show_window.set(audible_newlist)
conction = IntVar()
def audible_catagory():
new_pc = Toplevel(dash_1)
new_pc.title('Amazon Bestsellers PC')
Label(new_pc, text = "Amazon Bestsellers PC", font = ('Arial', 14),
bg='red', fg='floralwhite').pack()
Label(new_pc, textvariable = show_window, justify = LEFT, bg= 'light yellow',takefocus = True).pack()
Label(dash_1, text = "Amazon Bestsellers Audible", font = ('Arial', 14), bg='red', fg='floralwhite').pack()
Label(dash_1, text = show_window).pack()
Button(dash_1, text = 'Add to cart').pack()
Radiobutton(dash_1, text = 'Amazon Audible', value = True, variable = conction, command =audible_catagory).place(x=0, y=0)
spin_box = Spinbox(dash_1, from_=0, to=10, fg = 'black',
width = 2, justify = RIGHT, relief = 'flat', bg = 'red').pack()
dash_1.mainloop()
When you run this python code it creates a window, which has a radiobutton, and when that radiobutton clicked it pops up another window which displaying the items price and its name from 1 to 10 items. There is a spinbox when which has values from 1 to 10. There is also a button called add to cart.
How do I connect this spinbox to the rest of the code and the add to cart button. So when someone want to buy item number nine they will use the spinbox to select item nine and then when they press add to cart button the item will be added to a list. which will display the total cost of the items.
If anyone can help will be much appreciated.
So when you click the Add to Cart button, the program should first get the value of the Spinbox, then use that data to get the price of the item from a list and add its price to another variable.
You can use spin_box.get() to get the value from the Spinbox. To run a function on click, add the attribute command=your_function to Button(dash_1, text = 'Add to cart').pack(). Then you can add the function:
def your_function():
item_number = spinbox.get()
audible_cart_total_price += price_audible[item_number]
NOTE: You will have to change your data in price_audible to be numbers, not strings. You can use string interpolation to add the dollar signs back when displaying to the screen.

image can't be displayed from within function [duplicate]

This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 5 years ago.
I have a simple GUI tryout where I want to display few images on my screen. I start with setting the window with gray squares allover it and resizing the desired image and now I want to put it on the frame:
img = [img_resize(yellow_square, img_size), "text"]
base_panel = Label(root, image = img[0])
base_txt = Label(root, text = img[1])
base_panel.grid(row = x, column = y)
base_txt.grid (row = x, column = y)
...
for im in ls:
panel = Label(root, image = im[0])
panel.grid(row = x+im[1][1], column = y+im[1][2])
txt = Label(root, text = im[2])
txt.grid(row = x+im[1][1], column = y+im[1][2], sticky = 'S')
ls is a list [image (using Image.open(img)), location offset, text]
When I copy these lines to my main script, it all goes well and I get the desired
but when trying to do so from a function, I only get the text part to work
I guess something is wrong with my image = ... but I don't understand what, as I have the code working after I just copied these lines to main. The main has another image, so maybe this is affecting somehow?
This is the code from main:
for im in background: # background is a list of gray squares
# some manipulation an a,b
panel = Label(root, image = im)
panel.grid(row = a, column = b)
Here should come the function call or the lines themselves
This is a common problem. You have to keep a reference to the image, or else python deletes it from memory. If you aren't in a function then the variable is a global variable, which keeps the reference. But in a function you need to do something like this:
panel = Label(root)
panel.img = im[0]
panel.config(image = panel.img)

Is filling a grid with images so I could control location a good practice?

I'm trying to locate a few images on a grid, but when I try to do something like
panel = Label(root, image = img)
panel.grid(row = a, column = b)
where a,b are values that should bring the image to the center of the grid, I always get the image on the top left corner, because empty columns/rows don't fill the space. So, using a friend's advice, I added a blank image and did something like
a = 0
b = 0
for img in ls2: # ls2 is a list containing instances of the blank image
a += 1
if a == 11:
a = 1
b += 1
panel = Label(root, image = img)
panel.grid(row = a, column = b)
Now, when I'm trying to locate a new image on row = x, column = y it goes as I wanted and this is my current solution. My question is, is this a good way to enable me use the whole grid?
No, it's not a good way. There is no need to create invisible widgets. It's hard to say what the good way is, however, because "use the whole grid" is somewhat vague.
If by "use the whole grid" you mean want to create a grid where all rows are the same height, and all of the columns are the same width, and the grid fills its containing window, the right solution is to create uniform rows columns using rowconfigure and columnconfigure.
For example, if you want to create a 4x4 grid of equal sized cells, you could do something like this:
import tkinter as tk
root = tk.Tk()
for row in range(4):
root.grid_rowconfigure(row, uniform="default", weight=1)
for column in range(4):
root.grid_columnconfigure(column, uniform="default", weight=1)
label_2_2 = tk.Label(root, text="row 2, column 2", borderwidth=2, relief="groove")
label_1_0 = tk.Label(root, text="row1, column 0", borderwidth=2, relief="groove")
label_1_0.grid(row=1, column=0)
label_2_2.grid(row=2, column=2)
root.mainloop()
The uniform option takes any arbitrary string. Every row (or column) with the same value will have the same dimensions.
The weight option tells grid how to allocate any extra space. By giving each row and each column an identical non-zero weight, all extra space will be apportioned equally, causing the rows and columns to grow or shrink to fit the window.
Just found the place geometry manager. It is more explicit and lets me control the exact locations of my images inside the widget.
panel.place(x=100, y=100) gives pixel precision location which was what I looked for in the first place.

Resources