Python Forum
[Tkinter] I’m building a Python DataGridView (Excel-like UI for apps)—here’s a teaser.
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] I’m building a Python DataGridView (Excel-like UI for apps)—here’s a teaser.
#1
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
Reply
#2
I've been using vegaseat's for decades. Don't know if it is the same or not
buran write Nov-10-2025, 06:10 PM:
official clickbait warning revoked
buran write Nov-10-2025, 04:55 AM:
Link removed
Reply
#3
(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.
Reply
#4
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
Reply
#5
(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
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#6
(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.
Reply
#7
(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 Wink
« We can solve any problem by introducing an extra level of indirection »
Reply
#8
(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 Wink
OK if Vegaseat's covers your needs thats fine by me. Big Grin
Reply
#9
(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 »
Reply
#10
(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.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] How to Copy-Paste a table from Office apps to QTableView? Vittorio 5 11,148 Aug-05-2021, 11:14 AM
Last Post: Axel_Erfurt
  [PyQt] New To Python need to develop windows Desktop Database Apps r_ghamrawi 4 7,066 Jun-02-2017, 06:46 AM
Last Post: wavic

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020