{"id":19738075,"url":"https://github.com/merlinz01/ztable","last_synced_at":"2025-07-10T19:03:08.594Z","repository":{"id":236571059,"uuid":"792877950","full_name":"merlinz01/ZTable","owner":"merlinz01","description":"ZTable - the better Windows table widget","archived":false,"fork":false,"pushed_at":"2024-04-27T20:10:14.000Z","size":64,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-22T06:03:37.880Z","etag":null,"topics":["customizable","filterable","sortable","table","widget","win32","win32-c"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/merlinz01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-27T20:02:08.000Z","updated_at":"2025-05-04T03:54:12.000Z","dependencies_parsed_at":"2024-04-27T21:21:09.829Z","dependency_job_id":null,"html_url":"https://github.com/merlinz01/ZTable","commit_stats":null,"previous_names":["merlinz01/ztable"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/merlinz01/ZTable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merlinz01%2FZTable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merlinz01%2FZTable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merlinz01%2FZTable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merlinz01%2FZTable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/merlinz01","download_url":"https://codeload.github.com/merlinz01/ZTable/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merlinz01%2FZTable/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264637963,"owners_count":23642083,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["customizable","filterable","sortable","table","widget","win32","win32-c"],"created_at":"2024-11-12T01:13:11.588Z","updated_at":"2025-07-10T19:03:08.575Z","avatar_url":"https://github.com/merlinz01.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\r\n# ZTable - the better Windows table widget\r\n\r\nZTable is a easy-to-use Win32 table window with a more data-centric focus than Windows' builtin ListView.\r\nWritten in pure C with API exposed via window messages.\r\n\r\n![Demo screenshot](demo.png)\r\n\r\n## Win32 Usage\r\n\r\nJust call the `SetupClass` function to register the window class. \r\nThe class name of the table is \"ZTable\". \r\nMessage and notification constants are in `ztable.h`\r\n\r\n## Python Usage\r\n\r\nZTable is geared toward Python usage and therefore includes a Python class wrapper.\r\n\r\n### Note:\r\n\r\nThe Python class uses an unpublished home-made Win32 ctypes wrapper,\r\nbut it probably could be made to work with pywin32\r\nwith some tweaking.\r\n\r\nExample usage:\r\n\r\n```python\r\nfrom gui32.toplevel import Toplevel\r\nfrom ztable import *\r\n\r\nclass YourWindow(Toplevel):\r\n    def __init__(self):\r\n        ...\r\n        self.table = ZTable(self)\r\n        self.table.SetColumns(\r\n            self.table.MakeColumn(headerText=\"Column One\"),\r\n            self.table.MakeColumn(headerText=\"Column Two\"),\r\n            ...\r\n        )\r\n        for i in range(10):\r\n            self.table.InsertRow(-1, 'value one', 'value two', ...)\r\n        ...\r\n```\r\n\r\n## Features\r\n\r\n### Column alignment\r\n\r\nText can be aligned left, right, or center per column, except for the header text, which is always centered.\r\n\r\n### Resizable  columns\r\n\r\nIf a column does not have `ZTC_NORESIZE`, it can be resized by dragging their dividers with the mouse.\r\n\r\nOr you can set the width programmatically:\r\n\r\n```python\r\ntable.SetColumnWidth(column_index, 157)\r\n```\r\n\r\nThe width of a column can be specified on creation, along with a minimum and maximum width:\r\n\r\n```python\r\ntable.SetColumns(\r\n    table.MakeColumn(headerText='157 pixels wide', width=157, minWidth=20, maxWidth=350),\r\n    ...\r\n)\r\n```\r\n\r\nIf a column has `ZTC_DEFSIZEONRCLICK`, it will snap to its default size when its header is right-clicked.\r\nThe default size is set on creation:\r\n\r\n```python\r\ntable.SetColumns(\r\n    table.MakeColumn(headerText='Snaps to 100px width', defaultWidth=100),\r\n    ...\r\n)\r\n```\r\n\r\n### Table notifications\r\n\r\nThe table notifies of right-clicks anywhere on the window.\r\n\r\n```python\r\nclass YourWindow(Toplevel):\r\n    def __init__(self):\r\n        ...\r\n        self.table.OnRightClick = self.OnTableRightClicked\r\n        ...\r\n\r\n    def OnTableRightClicked(self, row_index, column_index, x, y):\r\n        # Post a popup menu at (x, y)\r\n        ...\r\n```\r\n\r\n### Column notifications\r\n\r\nColumns can be set to report double-click notifications with `ZTC_NOTIFYDBLCLICKS`.\r\nColumns that do not have `ZTC_DEFAULTSORT` report header clicks.\r\n\r\n```python\r\nclass YourWindow(Toplevel):\r\n    def __init__(self):\r\n        ...\r\n        self.table.OnDoubleClick = self.OnCellDoubleClicked\r\n        self.table.OnHeaderClicked = self.OnTableHeaderClicked\r\n        ...\r\n    \r\n    def OnTableHeaderClicked(self, x, y, column_index):\r\n        # Post a menu to select options\r\n        ...\r\n\r\n    def OnCellDoubleClicked(self, row_index, column_index, x, y):\r\n        # Open a dialog to show more details\r\n        ...\r\n```\r\n\r\n### Sorting\r\nColumns with `ZTC_DEFAULTSORT` automatically sort the table when their header is clicked.\r\nOtherwise, you can implement custom sorting by setting the `OnHeaderClicked` callback to a function that calls `Sort`, as shown:\r\n```python\r\nclass YourWindow(Toplevel):\r\n    def __init__(self):\r\n        ...\r\n        self.table.OnHeaderClicked = self.OnTableHeaderClicked\r\n        ...\r\n    \r\n    def OnTableHeaderClicked(self, row_index, column_index):\r\n        lParam = 0xc0ffee\r\n        reverse = -1 # Use opposite of current column reverse state\r\n        self.table.Sort(self.ComparisonCallback, column_index, reverse, lParam)\r\n\r\n    def ComparisonCallback(self, hwnd_table, itemptr_1, itemptr_2, column_index, lParam):\r\n        if lParam != 0xc0ffee:\r\n            print(\"I ordered coffee!\")\r\n        text1 = itemptr_1.contents.items[column_index].text.value\r\n        text2 = itemptr_2.contents.items[column_index].text.value\r\n        if text1 == text2:\r\n            return 0\r\n        elif text1 \u003e text2:\r\n            return 1\r\n        else:\r\n            return -1\r\n```\r\n\r\n### Editing\r\n\r\nColumns with `ZTC_EDITABLE` can edited by double-clicking a cell in that column. \r\nIf the editing notifications are not handled, simple string editing is performed.\r\nTo customize the editing process, set the `OnEditStart` and/or `OnEditEnd` callbacks, as shown:\r\n\r\n```python\r\nfrom gui32.edit import Edit\r\n\r\nclass YourWindow(Toplevel):\r\n    def __init__(self):\r\n        ...\r\n        self.table.OnEditStart = self.OnEditStart\r\n        self.table.OnEditEnd = self.OnEditEnd\r\n        ...\r\n    \r\n    def OnEditStart(self, row_index, column_index, edit_window_handle):\r\n        # The edit control currently has the text of the cell we are editing\r\n        edit = Edit.from_handle(edit_window_handle)\r\n        text = edit.Text\r\n        # We can set the text that appears in the edit control\r\n        edit.Text = \"Text being edited: \" + text.strip()\r\n\r\n    def OnEditEnd(self, row_index, column_index, edit_window_handle):\r\n        # The edit control currently has the text that the user entered\r\n        edit = Edit.from_handle(edit_window_handle)\r\n        text = edit.Text\r\n        # We can set the text that is set to the cell being edited\r\n        edit.Text = text.removeprefix(\"Text being edited: \")\r\n        # If we succeeded, return False\r\n        # Had we decided the text was invalid, we could return True \r\n        # to select the contents of the edit control and continue editing\r\n        return False\r\n```\r\n\r\nYou can start an editing process programmatically:\r\n\r\n```python\r\ntable.BeginEditing(row_index, column_index)\r\n```\r\n\r\nYou can end an editing process:\r\n\r\n```python\r\n# This will call table.OnEditEnd\r\n# It may or may not actually end the editing.\r\ntable.EndEditing()\r\n```\r\n\r\nYou can cancel an editing process:\r\n\r\n```python\r\n# This will not call table.OnEditEnd\r\n# The editing will end regardless of the text entered\r\ntable.CancelEditing()\r\n```\r\n\r\nYou can see if an editing procedure is currently active:\r\n\r\n```python\r\nprint(table.InEditing)\r\n```\r\n\r\nYou can combine these to make a one-shot try at ending \r\nthe editing procedure gracefully (e.g., when closing the program):\r\n\r\n```python\r\n# Is there an active editing procedure?\r\nif table.InEditing:\r\n    # If so, try to save the value\r\n    table.EndEditing()\r\n    # Did it work?\r\n    if table.InEditing:\r\n        # If not, cancel and go on\r\n        table.CancelEditing()\r\n```\r\n\r\n### Custom row colors\r\n\r\nAny row can either have default colors, or a custom text color and background fill brush.\r\nSet the colors as shown:\r\n\r\n```python\r\n# Set to default colors\r\ntable.SetRowColors(row_index)\r\n# Set text color to a medium gray\r\ntable.SetRowColors(row_index, textColor=0x808080)\r\n# Create a custom background brush\r\nfrom gui32.backend import winapi, wintypes\r\nred_checkered_brush = winapi.CreateBrushIndirect(\r\n    wintypes.LOGBRUSH(2, 0x0000ff, 5))\r\n# Set background fill to red checkers\r\ntable.SetRowColors(row_index, fillBrush=red_checkered_brush)\r\n# Set both text color and background fill\r\ntable.SetRowColors(row_index, textColor=0x808080, fillBrush=red_checkered_brush)\r\n# You can also set these when creating the row\r\ntable.InsertRow(-1, 'Values', ..., textColor=0x808080, fillBrush=red_checkered_brush)\r\n```\r\n\r\n### Custom cell colors\r\n\r\nIf a column has `ZTC_CUSTOMBG`, the user-defined parameter for each cell \r\nin that column is a win32 brush that is the background fill of the cell.\r\nIf the parameter is `0`, the default or row-specific background is used.\r\n\r\nIf a column has `ZTC_CUSTOMFG`, the user-defined parameter for each cell\r\nin that column is a `COLORREF` value that is the text color of the cell.\r\nIf the parameter is `CLR_DEFAULT`, the default or row-specific text color is used.\r\n\r\nA column cannot have both custom background and custom text color.\r\n\r\nExample:\r\n\r\n```python\r\ntable.SetColumns(\r\n    table.MakeColumn(headerText='Custom text color', flags=ZTC_CUSTOMFG),\r\n    table.MakeColumn(headerText='Custom background', flags=ZTC_CUSTOMBG),\r\n)\r\nfrom gui32.backend import winapi\r\ncolors = (\r\n    (0x101010, winapi.CreateSolidBrush(0xf0f0f0)),\r\n    (0x123456, winapi.CreateSolidBrush(0x654321)),\r\n    ...\r\n)\r\nfor color, brush in colors:\r\n    row_index = table.InsertRow(-1, 'howdee', 'do')\r\n    table.SetItemParam(row_index, 0, color)\r\n    table.SetItemParam(row_index, 1, brush)\r\n```\r\n\r\n### Row filtering\r\n\r\nRows can be hidden or shown arbitrarily, as shown:\r\n\r\n```python\r\n# Hide the row without losing any data\r\ntable.SetRowFiltered(row_index, False)\r\n# Show the row again\r\ntable.SetRowFiltered(row_index, True)\r\n```\r\n\r\nThis does not affect the internal row table or a row's associated index, \r\nso \r\n```python\r\nfor row_index in range(len(table)):\r\n    print(table.GetItemText(row_index, 0))\r\n```\r\nwill print data for hidden rows also.\r\n\r\n### Custom line height\r\n\r\nYou can set the height of rows in the table, in multiples of line height, as shown:\r\n\r\n```python\r\n# Make all the rows in the table 3 lines high\r\ntable.SetRowHeight(3)\r\n```\r\n\r\n### Multiline cells\r\n\r\nIf a column has `ZTC_MULTILINE`, the text in each cell will be drawn with `DT_WORDBREAK` flag set.\r\nThis means that `\\r\\n` line separators will cause line breaks. \r\nDue to the limited capabilities of the Win32 `DrawText` function, the text will not be vertically centered.\r\nThis feature is obviously only useful in conjunction with custom line height.\r\n\r\n### New row generation\r\n\r\nIf you turn on automatic row generation:\r\n```python\r\ntable.SetAutoMakeNewRow(True)\r\n```\r\nthen when an editing procedure has successfully finished in the last row in the table, \r\na new row is appended to the table.\r\nThe table sends a notification when it adds a row so that you can customize the contents of each new row.\r\nTo handle this notification:\r\n\r\n```python\r\nclass YourWindow(Toplevel):\r\n    def __init__(self):\r\n        ...\r\n        self.table.OnAutoNewRow = self.OnAutoNewRow\r\n        ...\r\n\r\n    def OnAutoNewRow(self, row_index):\r\n        self.table.SetItemText(row_index, 0, 'This row was added.')\r\n```\r\n\r\n### Row selector column\r\n\r\nIf the first column has `ZTC_SELECTOR`, it is displayed without cell text, with a uniform fill color, and with an arrow in the currently selected row.\r\nIf new-row generation is enabled, it displays a `*` icon in the last row.\r\n```python\r\ntable.SetColumns(\r\n    table.MakeColumn(flags=ZTC_SELECTOR),\r\n    ...\r\n)\r\n```\r\n\r\n## Features not implemented\r\n\r\n- Keybinding to start editing\r\n- Custom editing widgets - mostly done\r\n- Icons and bitmaps (at least check marks)? - supported sort-of for selector column\r\n- Allow setting default row colors?\r\n- Combined custom background and text color per cell\r\n- Custom borders per cell\r\n- Allow text (line numbers etc) in selector column, highlighted selection there\r\n\r\n## License\r\n\r\nMIT License","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerlinz01%2Fztable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmerlinz01%2Fztable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerlinz01%2Fztable/lists"}