Posts: 6
Threads: 1
Joined: Nov 2025
Nov-07-2025, 07:29 PM
(This post was last modified: Nov-07-2025, 07:29 PM by tsgiannis.)
Hey everyone,
I’ve been working on a side project that I think might solve a pain point for a lot of Python devs: a high-performance, Excel-like DataGridView for Python apps. If you’ve ever wanted to embed a powerful, customizable grid (think sorting, filtering, cell editing) into your tools—but without the bloat of Excel or the limitations of existing libraries—this is for you.
I put together a quick teaser video to show what’s coming:
Why I built this:
Most Python GUI frameworks lack advanced grid features out of the box.
I wanted something lightweight, fast, and easy to integrate (WinForms-style but for Python).
I’m tired of hacking together clunky workarounds for data-heavy UIs.
What’s special?
Designed for performance (handles large datasets smoothly).
Customizable UI (themes, cell types, etc.).
Works with your favorite backend (SQL, Pandas, you name it).
I’d love your feedback!
Would you use something like this?
What features would make it a must-have for your projects?
P.S1 Developed with ttkbootstrap, an extension of TKinter
P.S2. If this already exists and I’ve been living under a rock, let me know—I've other ideas to develop
Posts: 541
Threads: 0
Joined: Feb 2018
Nov-09-2025, 10:15 PM
(This post was last modified: Nov-10-2025, 06:10 PM by buran.)
I've been using vegaseat's for decades. Don't know if it is the same or not
buran write Nov-10-2025, 04:55 AM:Link removed
Posts: 6
Threads: 1
Joined: Nov 2025
(Nov-09-2025, 10:15 PM)woooee Wrote: I've been using vegaseat's for decades. Don't know if it is the same or not
If it works for you continue to use it, when I was looking I didn't find it.
Posts: 541
Threads: 0
Joined: Feb 2018
Nov-10-2025, 04:53 PM
(This post was last modified: Nov-10-2025, 04:54 PM by woooee.)
They removed the link to it, sorry. Don't know why unless they don't allow any links to external sites. Note that I have commented some of the code that I do not use.
class McListBox(object):
"""use a ttk.TreeView as a multicolumn ListBox"""
def __init__(self, root, header_list, data_list):
self.root=root
self.tree = None
self.header_list=header_list
self.data_list=data_list
self._setup_widgets()
self._build_tree()
tk.Button(self.root, text='Exit', bg="orange",
command=self.root.quit).grid(row=20)
self.selected_offsets=[]
def _setup_widgets(self):
# create a treeview with dual scrollbars
self.tree = ttk.Treeview(columns=self.header_list, show="headings",
selectmode="extended")
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')
vsb.grid(column=1, row=0, sticky='ns')
hsb.grid(column=0, row=1, sticky='ew')
self.tree.bind("<Up>", self.up_arrow)
def _build_tree(self):
for col in self.header_list:
self.tree.heading(col, text=col.title())
# adjust the column's width to the header string
self.tree.column(col,
width=font.Font().measure(col.title()))
self.item_id=[]
for item in self.data_list:
self.item_id.append(self.tree.insert('', 'end', values=item)) ## store tkinter id
# adjust column's width if necessary to fit each value
for ix, val in enumerate(item):
col_w = font.Font().measure(val)
if self.tree.column(self.header_list[ix],width=None) < col_w:
self.tree.column(self.header_list[ix], width=col_w)
## set the focus in the middle for testing
## new_id=self.item_id[5]
## self.tree.focus_set() ## sets focus to the treeview
## self.tree.selection_set((new_id, new_id)) ## updates background
## self.tree.focus(new_id) ## sets new id as focus
'''
def shift_up_arrow(self, event):
""" gets selected item(s) and prints thems
"""
id_selected=self.tree.focus()
for offset, id in enumerate(self.item_id):
if id==id_selected:
self.selected_offsets.append(offset)## save selection
print offset, self.data_list[offset]
## change background color
self.tree.delete(id_selected)
self.tree.insert('', offset, id_selected,
values=self.data_list[offset], tags=("ABC",))
self.tree.tag_configure("ABC", background='yellow')
new_id=self.item_id[0]
if offset > 0:
new_id=self.item_id[offset]
self.tree.focus_set() ## sets focus to the treeview
self.tree.selection_set((new_id, new_id)) ## updates background
self.tree.focus(new_id) ## sets new id as focus
return
'''
def up_arrow(self, event):
id_selected=self.tree.focus()
for offset, id in enumerate(self.item_id):
if id==id_selected and offset > 0:
new_id=self.item_id[offset]
self.tree.focus_set() ## sets focus to the treeview
self.tree.selection_set((new_id, new_id)) ## updates background
self.tree.focus(new_id) ## sets new id as focus
if __name__ == "__main__":
# the test data ...
header_list = ['car', 'repair']
car_list = [
('Hyundai', 'brakes') ,
('Honda', 'light') ,
('Lexus', 'battery') ,
('Benz', 'wiper') ,
('Ford', 'tire') ,
('Chevy', 'air') ,
('Chrysler', 'piston') ,
('Toyota', 'brake pedal') ,
('BMW', 'seat'),
('Audi', 'starter'),
('Fiat', 'shocks'),
('Tesla', 'battery,'),
('Peugeot', 'transmission'),
('Porsche', 'fuel pump')
]
root = tk.Tk()
root.wm_title("multicolumn ListBox")
mc_listbox = McListBox(root, header_list, car_list)
root.mainloop()
Gribouillis likes this post
Posts: 8,204
Threads: 162
Joined: Sep 2016
(Nov-10-2025, 04:53 PM)woooee Wrote: They removed the link to it, sorry. Don't know why unless they don't allow any links external sites. I removed the link because it looked like link to random thread on external forum. Now I see that you refer to a specific post buried down the thread. In any case posting it here is better as the other site may be down in the future
Posts: 6
Threads: 1
Joined: Nov 2025
(Nov-10-2025, 04:53 PM)woooee Wrote: They removed the link to it, sorry. Don't know why unless they don't allow any links to external sites. Note that I have commented some of the code that I do not use.
class McListBox(object):
"""use a ttk.TreeView as a multicolumn ListBox"""
def __init__(self, root, header_list, data_list):
self.root=root
self.tree = None
self.header_list=header_list
self.data_list=data_list
self._setup_widgets()
self._build_tree()
tk.Button(self.root, text='Exit', bg="orange",
command=self.root.quit).grid(row=20)
self.selected_offsets=[]
def _setup_widgets(self):
# create a treeview with dual scrollbars
self.tree = ttk.Treeview(columns=self.header_list, show="headings",
selectmode="extended")
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')
vsb.grid(column=1, row=0, sticky='ns')
hsb.grid(column=0, row=1, sticky='ew')
self.tree.bind("<Up>", self.up_arrow)
def _build_tree(self):
for col in self.header_list:
self.tree.heading(col, text=col.title())
# adjust the column's width to the header string
self.tree.column(col,
width=font.Font().measure(col.title()))
self.item_id=[]
for item in self.data_list:
self.item_id.append(self.tree.insert('', 'end', values=item)) ## store tkinter id
# adjust column's width if necessary to fit each value
for ix, val in enumerate(item):
col_w = font.Font().measure(val)
if self.tree.column(self.header_list[ix],width=None) < col_w:
self.tree.column(self.header_list[ix], width=col_w)
## set the focus in the middle for testing
## new_id=self.item_id[5]
## self.tree.focus_set() ## sets focus to the treeview
## self.tree.selection_set((new_id, new_id)) ## updates background
## self.tree.focus(new_id) ## sets new id as focus
'''
def shift_up_arrow(self, event):
""" gets selected item(s) and prints thems
"""
id_selected=self.tree.focus()
for offset, id in enumerate(self.item_id):
if id==id_selected:
self.selected_offsets.append(offset)## save selection
print offset, self.data_list[offset]
## change background color
self.tree.delete(id_selected)
self.tree.insert('', offset, id_selected,
values=self.data_list[offset], tags=("ABC",))
self.tree.tag_configure("ABC", background='yellow')
new_id=self.item_id[0]
if offset > 0:
new_id=self.item_id[offset]
self.tree.focus_set() ## sets focus to the treeview
self.tree.selection_set((new_id, new_id)) ## updates background
self.tree.focus(new_id) ## sets new id as focus
return
'''
def up_arrow(self, event):
id_selected=self.tree.focus()
for offset, id in enumerate(self.item_id):
if id==id_selected and offset > 0:
new_id=self.item_id[offset]
self.tree.focus_set() ## sets focus to the treeview
self.tree.selection_set((new_id, new_id)) ## updates background
self.tree.focus(new_id) ## sets new id as focus
if __name__ == "__main__":
# the test data ...
header_list = ['car', 'repair']
car_list = [
('Hyundai', 'brakes') ,
('Honda', 'light') ,
('Lexus', 'battery') ,
('Benz', 'wiper') ,
('Ford', 'tire') ,
('Chevy', 'air') ,
('Chrysler', 'piston') ,
('Toyota', 'brake pedal') ,
('BMW', 'seat'),
('Audi', 'starter'),
('Fiat', 'shocks'),
('Tesla', 'battery,'),
('Peugeot', 'transmission'),
('Porsche', 'fuel pump')
]
root = tk.Tk()
root.wm_title("multicolumn ListBox")
mc_listbox = McListBox(root, header_list, car_list)
root.mainloop()
Have your tried it loading 570K lines of CSV (or whatever) to check the performance?
In the video just for fun I load a 570,000 lines CSV and it loads instantly and scrolls likes a charm :)
But I will examine your code, maybe I find something useful.
Posts: 4,904
Threads: 79
Joined: Jan 2018
Nov-10-2025, 07:46 PM
(This post was last modified: Nov-10-2025, 07:47 PM by Gribouillis.)
(Nov-10-2025, 07:01 PM)tsgiannis Wrote: Have your tried it loading 570K lines of CSV (or whatever) to check the performance? The problem with your code is that we don't have the code, only promises.
(Nov-09-2025, 10:15 PM)woooee Wrote: I've been using vegaseat's for decades. After 14 years, Vegaseat's code still works like a charm
« We can solve any problem by introducing an extra level of indirection »
Posts: 6
Threads: 1
Joined: Nov 2025
Nov-11-2025, 08:25 AM
(This post was last modified: Nov-11-2025, 08:27 AM by tsgiannis.)
(Nov-10-2025, 07:46 PM)Gribouillis Wrote: (Nov-10-2025, 07:01 PM)tsgiannis Wrote: Have your tried it loading 570K lines of CSV (or whatever) to check the performance? The problem with your code is that we don't have the code, only promises.
(Nov-09-2025, 10:15 PM)woooee Wrote: I've been using vegaseat's for decades. After 14 years, Vegaseat's code still works like a charm  OK if Vegaseat's covers your needs thats fine by me.
Posts: 4,904
Threads: 79
Joined: Jan 2018
(Nov-11-2025, 08:25 AM)tsgiannis Wrote: OK if Vegaseat's covers your needs thats fine by me. Vegaseat's code covers my needs much more than your non-existent library. I noticed that you posted the same teaser in Daniweb and in Reddit, still without a link to a repository. What's the purpose of all this noise if you don't provide code ?
« We can solve any problem by introducing an extra level of indirection »
Posts: 6
Threads: 1
Joined: Nov 2025
(Nov-11-2025, 08:36 AM)Gribouillis Wrote: (Nov-11-2025, 08:25 AM)tsgiannis Wrote: OK if Vegaseat's covers your needs thats fine by me. Vegaseat's code covers my needs much more than your non-existent library. I noticed that you posted the same teaser in Daniweb and in Reddit, still without a link to a repository. What's the purpose of all this noise if you don't provide code ?
Checking if is of any interest, if not I will dump it.
|