Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add DataTable widget #106

Closed
israel-dryer opened this issue Dec 29, 2021 · 6 comments · Fixed by #110
Closed

add DataTable widget #106

israel-dryer opened this issue Dec 29, 2021 · 6 comments · Fixed by #110

Comments

@israel-dryer
Copy link
Owner

@israel-dryer

Very useful column sorting option. It would be nice if you could sort columns with the following types of information
string, integers, floats, date and time, codes, currency, percentage

Some Text, 5, 12.1, 2021-08-31, 2.5.2.1, U$5.30, 15%

codes may have several and mixed separators (. - /) = (8980.046.688.998.890) (156.799.179-39) (00776574/0001-56)
However, there are some regional peculiarities of each country.
Standard Currency Type - U$, £$, R$ ....
Date format - there are several date formations (datetime.datetime.strptime(string, format)) where format='%Y-%m-%d' or format='%d/%m/%Y' ...
Decimal Separator - Floatad can use semicolons (12.1 or 12,1)

see an example of sort (incomplete)
https://stackoverflow.com/questions/69778143/python-treeview-alternate-row-colouring-after-sorting


Another useful option would be to color the lines in the treeview. And differentiate the even lines from the odd ones creating a zebra effect.
main background color
Color when selecting line
Even line color (primary)
Odd thread color (secondary)


A button to update table data and
Instead of a combobox to display the page as I suggested earlier.
It would be more interesting an entry to type the desired page as in the image:
01


Another more advanced option would be a submenu in the header to show or hide the columns in the treeview. (I don't know if this would be useful for all people in all cases)

Here is an image of a Crud with this feature.

Clicking on the header sorts the data.

With the mouse over in the header, it displays a sub-menu option with the following data:
02
Sort in ascending order
Sort in descending order
Columns > another subemnu with the treeview column names [check item]

Originally posted by @antrrax in #77 (comment)

@israel-dryer israel-dryer changed the title add DataTable widet add DataTable widget Dec 29, 2021
@israel-dryer
Copy link
Owner Author

@daniilS, @antrrax making progress on the DataTable, but have a question on implementing a feature.
https://github.com/israel-dryer/ttkbootstrap/blob/data-table/development/new_widgets/datatable.py

I've added autowidth and autoalign parameters which set the width and alignment of the columns and headers automatically. Text is aligned left, and numbers right. I want to be able to double click the space between the headers and call the autowidth method. However, I'm not quite sure how to bind that component. You have any ideas?

Oh yeah... I added striped rows as well.

python_34OZNRzviF

@antrrax
Copy link
Contributor

antrrax commented Dec 29, 2021

@israel-dryer

See this example, it might help. Have the option to resize the column by double clicking on the border between the column headers.

https://codereview.stackexchange.com/questions/243799/tkinter-treeview
https://github.com/unodan/TkInter-Treeview-Example-Demo

class DataTable(ttk.Frame):
    def __init__(
        ...
        self.bind('<Double-Button-1>', self.button_double_click)


    def button_double_click(self, event=None):
        region = self.identify_region(event.x, event.y)
        print(region)

        if region == 'separator':
            print('autowidth Here')

If possible you could add the automatic Horizontal scrollbar, for cases with many columns beyond the window size. And also an automatic vertical scrollbar.


Thanks for the implementations it's looking really good. Tkinter needed such a feature-rich widget...

--
Complement:

You can attach the <KP_Enter> event to the same functions as 'Return'. ?

Here I use Linux Mint 20.2 x64 Cinnamon X11 and the numeric keyboard's enter button <KP_Enter> doesn't work natively, it needs to be manually attached. Maybe an Ubuntu/Mint or Tkinter bug

example:

 index.bind("<Return>", self.goto_page)
 index.bind("<KP_Enter>", self.goto_page)

@israel-dryer
Copy link
Owner Author

I've made a lot of these recommendations. Added the <KP_Enter> bind as well...I had forgotten about that one.

Here is the current progress. I'm going to do some more testing on Mac OS and Linux. Most of the menu commands are setup for event binding, so if you have some recommendations for some keyboard shortcuts I can implement those easily as well.

Below you can see the table body right-click menu with lots of built-in options. It's definitely not a spreadsheet by any stretch, but it is very feature rich compared to what you normally get out-of-the-box with a Treeview widget.

tableview-1

The column right-click menu has a few options as well that are more relevant to column headers and columns.

tableview-2

I decided against the scrollbar for the moment; I haven't made my mind up about that one yet. I believe you can only use it if you turn off Flex on the columns. But, I'll keep it in mind as an option.

I'm using unicode characters for "images" at the moment. But I'm hoping sometime in the next few versions I'll be able to integrate svg and that will open up a lot more options for built-in icon support... and those will look A LOT better.

@antrrax
Copy link
Contributor

antrrax commented Jan 1, 2022

### Congratulations this widget is fantastic! very complete


I did a test run with the old version you posted on:
https://github.com/israel-dryer/ttkbootstrap/blob/data-table/development/new_widgets/datatable.py

And I noticed a peculiar behavior when resizing the columns.
https://www.mediafire.com/file/bti1s4kjgj9e8jb/autosize_columns_behavior.mp4/file

It starts with the proper size of the columns, however some columns are hidden off screen.
The sort icon (arrow) is visible
Double-clicking on the header separator resizes the columns to fit the screen (stacks the content if there is no space on the screen)
(The expected behavior when double-clicking the header separator is that resizing will fit the column to the size of the header/column content, but it is resizing to fit the content in the window)

When resizing the panedwindow, the treeview columns regain their size adequate to the content, but with some columns hidden off the screen..

Was this autosize_columns designed that way anyway, or is it a bug?


https://www.mediafire.com/file/bqcbqrkrxfcjuyj/paging_error.mp4/file
I also noticed that when there is only one page (fewer items than pagesize), and clicking 'last button' creates a bug in pagination.


sample code:


from tkinter import *
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from widgets.dt import *

class Defaultframe(ttk.Window):
    def __init__(self):
        ttk.Window.__init__(self)
        self.theme_default = 'sandstone'
        ttk.Style(self.theme_default)

        from frm_main import Wmain
        self.frame = Wmain(self)
        self.frame.pack(fill=BOTH, expand=True)
        return


    def change_frm(self, frame, **kwargs):
        self.frame.pack_forget()
        self.frame = frame(self, **kwargs)
        self.frame.pack(fill=BOTH, expand=True)
        return

#-------------------




class Func(ttk.Frame):
    def window_coordinates(self):
        w = 1000
        h = 600
        ws = self.master.winfo_screenwidth()
        hs = self.master.winfo_screenheight()
        cx = int( (ws/2) - (w/2) )
        cy = int( (hs/2) - (h/2) )
        return w, h, cx, cy


class Wmain(Func):
    def __init__(self, master=None, **kwargs):
        self.data = kwargs.pop('data', None)

        ttk.Frame.__init__(self, master, **kwargs)
        self.style = ttk.Style()

        self.set_screen()
        self.pnl_left()
        self.pnl_right()
        return


    def set_screen(self):
        w, h, cx, cy = self.window_coordinates()

        self.master.geometry(f'''{w}x{h}+{cx}+{cy}''')
        self.master.title('Tkinter')
        return


    def pnl_left(self):
        self.pw_left = PanedWindow(self,borderwidth=0, background=self.style.colors.dark)
        self.pw_left.pack(fill=BOTH, expand=1)
        
        self.frame_left = ttk.Frame(self.pw_left, bootstyle='primary')

        self.pw_left.add(self.frame_left)
        self.pw_left.paneconfig(self.frame_left, minsize=73, width=187)

        self.btn_admin = ttk.Button(self.frame_left, text='Admin')
        self.btn_admin.pack(fill=X, ipady=8, pady=1)
        self.btn_admin.configure(style='warning.TButton')

        self.btn_register = ttk.Button(self.frame_left, text='Peoples')
        self.btn_register.pack(fill=X, ipady=8, pady=1)

        self.btn_report = ttk.Button(self.frame_left, text='Report')
        self.btn_report.pack(fill=X, ipady=8, pady=1)

        return


    def pnl_right(self):
        self.frm_ctt = ttk.Frame(self, bootstyle='light')
        self.frm_ctt.place(relx=0, rely=0, relwidth=1, relheight=1)

        self.pw_left.add(self.frm_ctt)

        coldata = ['cod_id', 'xxxxxuser', 'expire', 'email', 'num', 'yyyyyyyyyyyyyyyyy', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz', 'xxxxxxxxxxxxxxxxxxxxx']
        result = [
            [1, 'admin', 0, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 0, 1, 2, 3], 
            [2, 'user', 1, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 0, 1, 2, 3], 
            [3, 'nono', 1, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 0, 1, 2, 3]
        ]


        self.style.configure("symbol.Link.TButton", font="-size 16")
        self.dt = Tableview(
            master=self.frm_ctt,
            coldata=coldata,
            rowdata=result,
            paginated=True,
            searchable=False,
            bootstyle='dark',
            stripecolor=('#f3f3f3', self.style.colors.fg)
        )
        self.dt.place(relx=0.02, y=25, relwidth=0.95, relheight=0.6)

        return


if __name__== '__main__':
    app = Defaultframe()
    app.mainloop()

@israel-dryer
Copy link
Owner Author

Thanks. Ok, so it appears having a scrollbar is going to be a requirement. Also, I'll need to turn off the default 'stretch' parameter. Stretch, which is True for treeview widgets by default, causes the columns to size relative to the available space in the window, which basically turns off the horizontal scrolling.

Also, it appears that when calculating the width of the column using font.Font().measure, I need to account for the scaled pixel width of the padding in the header and columns. I think I can fix this by adding a scaled factor of 10 (which is the combined padding in the style for left and right). This will prevent the columns from getting cutoff at the end.

I think I'm going to rename 'autosize' to 'autofit', which is the spreadsheet naming convention for Excel... I think people will understand more easily what that means.

What do you think of having a readonly combobox that allows you to set the page size? This would be different than setting the height. I'll leave height as is currently, which sets the visible rows in view, while the page size sets how many records are available on the scrollable page.

image

Another option is to set the layout similar to this material design version

image

@antrrax
Copy link
Contributor

antrrax commented Jan 2, 2022

What do you think of having a readonly combobox that allows you to set the page size? This would be different than setting the height. I'll leave height as is currently, which sets the visible rows in view, while the page size sets how many records are available on the scrollable page.

A combobox with pagesize (Row per page) and the horizontal scroll bar would be intuitive for the user.

@israel-dryer israel-dryer linked a pull request Jan 3, 2022 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants