├── .github
└── FUNDING.yml
├── CTkTable
├── __init__.py
└── ctktable.py
├── LICENSE
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | ko_fi: akascape
4 |
5 |
--------------------------------------------------------------------------------
/CTkTable/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | CustomTkinter Table widget
3 | Author: Akash Bora
4 | License: MIT
5 | This is a custom table widget for customtkinter.
6 | Homepage: https://github.com/Akascape/CTkTable
7 | """
8 |
9 | __version__ = '1.1'
10 |
11 | from .ctktable import CTkTable
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Akash Bora
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CTkTable
2 |
3 | **Here is a quick and simple table widget having all the basic features.**
4 |
5 | 
6 |
7 | ## Features:
8 | - Add columns/rows
9 | - Delete columns/rows
10 | - Edit rows/columns at once
11 | - Insert values to specific cell
12 | - delete values from specific cell
13 | - update all values at once
14 | - edit each cell value and options
15 | - entry editing
16 | - can be used with scrollable frame
17 | - Lots of other data operations
18 |
19 | ## Installation
20 | ```
21 | pip install CTkTable
22 | ```
23 |
24 | ### [
](https://github.com/Akascape/CTkTable/archive/refs/heads/main.zip)
25 |
26 | _Dont forget to leave a ⭐_
27 |
28 | ## Usage
29 | ```python
30 | import customtkinter
31 | from CTkTable import *
32 |
33 | root = customtkinter.CTk()
34 |
35 | value = [[1,2,3,4,5],
36 | [1,2,3,4,5],
37 | [1,2,3,4,5],
38 | [1,2,3,4,5],
39 | [1,2,3,4,5]]
40 |
41 | table = CTkTable(master=root, row=5, column=5, values=value)
42 | table.pack(expand=True, fill="both", padx=20, pady=20)
43 |
44 | root.mainloop()
45 | ```
46 |
47 | ## Methods
48 | - **.insert(row, column, value, *args)**: change specific cell index data
49 | - **.add_row(index, values)**
50 | - **.add_column(index, values)**
51 | - **.edit_row(row_num, *args)**: edit one full row at once
52 | - **.edit_column(column_num, *args)**: edit one full column at once
53 | - **.delete_row(index)**: remove one row
54 | - **.delete_column(index)**: remove one column
55 | - **.delete_rows(indices)**: remove mutliple rows
56 | - **.delete_columns(indices)**: remove multiple columns
57 | - **.edit(row, column)**: edit specific cell without changing the value
58 | - **.select(row, column)**: select one cell
59 | - **.select_row(row)**: select a row
60 | - **.get_selected_row()**: get the values of the selected row
61 | - **.deselect_row(row)**: deselect a row
62 | - **.select_column(column)**: select a column
63 | - **.get_selected_column()**: get the values of selected column
64 | - **.deselect_column(column)**: deselect a column
65 | - **.update_values(values)**: update all values at once
66 | - **.delete(row, column, *args)**: delete the data from specific index
67 | - **.get()**: get all values
68 | - **.get(row, column)**: get specific cell value
69 | - **.get_row(row)**: get all values of a specific row
70 | - **.get_column(column)**: get all values of a specific column
71 | - **.configure(arguments)**: change other table attributes
72 |
73 | _here, **args** means ctkbutton parameters which can also be passed_
74 |
75 | **Note: treat all the table cells as a ctkbutton class**
76 |
77 | ## Arguments
78 | | Parameter | Description |
79 | |-----------| ------------|
80 | | **master** | parent widget |
81 | | **values** | the default values for table |
82 | | row | **optional**, set number of default rows |
83 | | column | **optional**, set number of default columns |
84 | | padx | add internal padding in x |
85 | | pady | add internal padding in y |
86 | | colors | set two fg_colors for the table (list), eg: `colors=["yellow", "green"]` |
87 | | color_phase | set color phase based on rows or columns, eg: `color_phase="vertical"` |
88 | | orientation | change the orientation of table, `vertical or horizontal` |
89 | | header_color | define the topmost row color |
90 | | corner_radius | define the corner roundness of the table |
91 | | hover_color | enable hover effect on the cells |
92 | | wraplength | set the width of cell text |
93 | | justify | anchor the position of the cell text |
94 | | **command** | specify a command when a table cell is pressed, [returns row, column, value] |
95 | | **other button parameters* | all other ctk button parameters can be passed |
96 |
97 | Note: This library is at early stage so there can be some performance issues.
98 | ### Thanks for visiting! Hope it will help :)
99 |
--------------------------------------------------------------------------------
/CTkTable/ctktable.py:
--------------------------------------------------------------------------------
1 | # CTkTable Widget by Akascape
2 | # License: MIT
3 | # Author: Akash Bora
4 |
5 | import customtkinter
6 | import copy
7 |
8 | class CTkTable(customtkinter.CTkFrame):
9 | """ CTkTable Widget """
10 |
11 | def __init__(
12 | self,
13 | master: any,
14 | row: int = None,
15 | column: int = None,
16 | padx: int = 1,
17 | pady: int = 0,
18 | width: int = 140,
19 | height: int = 28,
20 | values: list = None,
21 | colors: list = [None, None],
22 | orientation: str = "horizontal",
23 | color_phase: str = "horizontal",
24 | border_width: int = 0,
25 | text_color: str or tuple = None,
26 | border_color: str or tuple = None,
27 | font: tuple = None,
28 | header_color: str or tuple = None,
29 | corner_radius: int = 25,
30 | write: str = False,
31 | command = None,
32 | anchor: str = "c",
33 | hover_color: str or tuple = None,
34 | hover: bool = False,
35 | justify: str = "center",
36 | wraplength: int = 1000,
37 | **kwargs):
38 |
39 | super().__init__(master, fg_color="transparent")
40 |
41 | if values is None:
42 | values = [[None,None],[None,None]]
43 |
44 | self.master = master # parent widget
45 | self.rows = row if row else len(values) # number of default rows
46 | self.columns = column if column else len(values[0])# number of default columns
47 | self.width = width
48 | self.height = height
49 | self.padx = padx # internal padding between the rows/columns
50 | self.pady = pady
51 | self.command = command
52 | self.values = values # the default values of the table
53 | self.colors = colors # colors of the table if required
54 | self.header_color = header_color # specify the topmost row color
55 | self.phase = color_phase
56 | self.corner = corner_radius
57 | self.write = write
58 | self.justify = justify
59 | self.binded_objects = []
60 |
61 | if self.write:
62 | border_width = border_width=+1
63 |
64 | if hover_color is not None and hover is False:
65 | hover=True
66 |
67 | self.anchor = anchor
68 | self.wraplength = wraplength
69 | self.hover = hover
70 | self.border_width = border_width
71 | self.hover_color = customtkinter.ThemeManager.theme["CTkButton"]["hover_color"] if hover_color is None else hover_color
72 | self.orient = orientation
73 | self.border_color = customtkinter.ThemeManager.theme["CTkButton"]["border_color"] if border_color is None else border_color
74 | self.inside_frame = customtkinter.CTkFrame(self, border_width=0, fg_color="transparent")
75 | super().configure(border_color=self.border_color, border_width=self.border_width, corner_radius=self.corner)
76 | self.inside_frame.pack(expand=True, fill="both", padx=self.border_width, pady=self.border_width)
77 |
78 | self.text_color = customtkinter.ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else text_color
79 | self.font = font
80 | # if colors are None then use the default frame colors:
81 | self.data = {}
82 | self.fg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if not self.colors[0] else self.colors[0]
83 | self.fg_color2 = customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"] if not self.colors[1] else self.colors[1]
84 |
85 | if self.colors[0] is None and self.colors[1] is None:
86 | if self.fg_color==self.master.cget("fg_color"):
87 | self.fg_color = customtkinter.ThemeManager.theme["CTk"]["fg_color"]
88 | if self.fg_color2==self.master.cget("fg_color"):
89 | self.fg_color2 = customtkinter.ThemeManager.theme["CTk"]["fg_color"]
90 |
91 | self.frame = {}
92 | self.corner_buttons = {}
93 | self.draw_table(**kwargs)
94 |
95 | def draw_table(self, **kwargs):
96 |
97 | """ draw the table """
98 | for i in range(self.rows):
99 | for j in range(self.columns):
100 | self.inside_frame.grid_rowconfigure(i, weight=1)
101 | self.inside_frame.grid_columnconfigure(j, weight=1)
102 | if self.phase=="horizontal":
103 | if i%2==0:
104 | fg = self.fg_color
105 | else:
106 | fg = self.fg_color2
107 | else:
108 | if j%2==0:
109 | fg = self.fg_color
110 | else:
111 | fg = self.fg_color2
112 |
113 | if self.header_color:
114 | if self.orient=="horizontal":
115 | if i==0:
116 | fg = self.header_color
117 | else:
118 | if j==0:
119 | fg = self.header_color
120 |
121 | corner_radius = self.corner
122 | if (self.border_width>=5) and (self.corner>=5):
123 | tr = self.border_color
124 | else:
125 | tr = ""
126 | if i==0 and j==0:
127 | corners = [tr, fg, fg, fg]
128 | hover_modify = self.hover
129 |
130 | elif i==self.rows-1 and j==self.columns-1:
131 | corners = [fg ,fg, tr, fg]
132 | hover_modify = self.hover
133 |
134 | elif i==self.rows-1 and j==0:
135 | corners = [fg ,fg, fg, tr]
136 | hover_modify = self.hover
137 |
138 | elif i==0 and j==self.columns-1:
139 | corners = [fg, tr, fg, fg]
140 | hover_modify = self.hover
141 |
142 | else:
143 | corners = [fg, fg, fg, fg]
144 | corner_radius = 0
145 | hover_modify = False
146 |
147 | if i==0:
148 | pady = (0, self.pady)
149 | else:
150 | pady = self.pady
151 |
152 | if j==0:
153 | padx = (0, self.padx)
154 | else:
155 | padx = self.padx
156 |
157 | if i==self.rows-1:
158 | pady = (self.pady,0)
159 |
160 | if j==self.columns-1:
161 | padx = (self.padx,0)
162 |
163 | if self.values:
164 | try:
165 | if self.orient=="horizontal":
166 | value = self.values[i][j]
167 | else:
168 | value = self.values[j][i]
169 | except IndexError: value = " "
170 | else:
171 | value = " "
172 |
173 | if value=="":
174 | value = " "
175 |
176 | if (i,j) in self.data.keys():
177 | if self.data[i,j]["args"]:
178 | args = self.data[i,j]["args"]
179 | else:
180 | args = copy.deepcopy(kwargs)
181 | else:
182 | args = copy.deepcopy(kwargs)
183 |
184 |
185 | self.data[i,j] = {"row": i, "column" : j, "value" : value, "args": args}
186 |
187 | args = self.data[i,j]["args"]
188 |
189 | if "text_color" not in args:
190 | args["text_color"] = self.text_color
191 | if "height" not in args:
192 | args["height"] = self.height
193 | if "width" not in args:
194 | args["width"] = self.width
195 | if "fg_color" not in args:
196 | args["fg_color"] = fg
197 | if args["fg_color"]!=fg:
198 | args["fg_color"] = fg
199 | if "corner_radius" in args:
200 | del args["corner_radius"]
201 | if "border_color" in args:
202 | del args["border_color"]
203 | if "border_width" in args:
204 | del args["border_width"]
205 | if "color_phase" in args:
206 | del args["color_phase"]
207 | if "orientation" in args:
208 | del args["orientation"]
209 | if "write" in args:
210 | del args["write"]
211 |
212 | if self.write:
213 | if "anchor" in args:
214 | del args["anchor"]
215 | if "hover_color" in args:
216 | del args["hover_color"]
217 | if "hover" in args:
218 | del args["hover"]
219 | if "justify" not in args:
220 | args["justify"] = self.justify
221 |
222 | self.frame[i,j] = customtkinter.CTkEntry(self.inside_frame,
223 | font=self.font,
224 | corner_radius=0,
225 | **args)
226 | if value is None:
227 | value = " "
228 | self.frame[i,j].insert(0, str(value))
229 | self.frame[i,j].bind("", lambda e, row=i, column=j, data=self.data: self.after(100, lambda: self.manipulate_data(row, column)))
230 | self.frame[i,j].grid(column=j, row=i, padx=padx, pady=pady, sticky="nsew")
231 |
232 | if self.header_color:
233 | if i==0:
234 | self.frame[i,j].configure(state="readonly")
235 |
236 | else:
237 | if "anchor" not in args:
238 | args["anchor"] = self.anchor
239 | if "hover_color" not in args:
240 | args["hover_color"] = self.hover_color
241 | if "hover" not in args:
242 | args["hover"] = self.hover
243 | if "justify" in args:
244 | anchor = args["justify"]
245 | if anchor=="center":
246 | anchor="c"
247 | elif anchor=="left":
248 | anchor="w"
249 | elif anchor=="right":
250 | anchor="e"
251 | args.update({"anchor": anchor})
252 | del args["justify"]
253 | if value is None:
254 | value = " "
255 | self.frame[i,j] = customtkinter.CTkButton(self.inside_frame, background_corner_colors=corners,
256 | font=self.font,
257 | corner_radius=corner_radius,
258 | text=value,
259 | border_width=0,
260 | command=(lambda e=self.data[i,j]: self.command(e)) if self.command else None, **args)
261 | self.frame[i,j].grid(column=j, row=i, padx=padx, pady=pady, sticky="nsew")
262 | if self.frame[i,j]._text_label is not None:
263 | self.frame[i,j]._text_label.config(wraplength=self.wraplength)
264 |
265 | if hover_modify:
266 | self.dynamic_hover(self.frame[i,j], i, j)
267 |
268 | self.rowconfigure(i, weight=1)
269 | self.columnconfigure(j, weight=1)
270 | for x in self.frame:
271 | for y in self.binded_objects:
272 | self.frame[x].bind(*y)
273 |
274 | def dynamic_hover(self, frame, i, j):
275 | """ internal function to change corner cell colors """
276 | self.corner_buttons[i,j] = frame
277 | fg = self.data[i,j]["args"]["fg_color"]
278 | hv = self.data[i,j]["args"]["hover_color"]
279 | if (self.border_width>=5) and (self.corner>=5):
280 | tr = self.border_color
281 | else:
282 | tr = ""
283 | if i==0 and j==0:
284 | corners = [tr, fg, fg, fg]
285 | hover_corners = [tr, hv, hv, hv]
286 | elif i==self.rows-1 and j==self.columns-1:
287 | corners = [fg ,fg, tr, fg]
288 | hover_corners = [hv, hv, tr, hv]
289 | elif i==self.rows-1 and j==0:
290 | corners = [fg ,fg, fg, tr]
291 | hover_corners = [hv, hv, hv, tr]
292 | elif i==0 and j==self.columns-1:
293 | corners = [fg, tr, fg, fg]
294 | hover_corners = [hv, tr, hv, hv]
295 | else:
296 | return
297 |
298 | frame.configure(background_corner_colors=corners, fg_color=fg)
299 | frame.bind("", lambda e, x=i, y=j, color=hover_corners, fg=hv:
300 | self.frame[x,y].configure(background_corner_colors=color, fg_color=fg))
301 | frame.bind("", lambda e, x=i, y=j, color=corners, fg=fg:
302 | self.frame[x,y].configure(background_corner_colors=color, fg_color=fg))
303 |
304 | def manipulate_data(self, row, column):
305 | """ entry callback """
306 | self.update_data()
307 | data = self.data[row,column]
308 | if self.command: self.command(data)
309 |
310 | def update_data(self):
311 | """ update the data when values are changes """
312 | for i in self.frame:
313 | if self.write:
314 | self.data[i]["value"]=self.frame[i].get()
315 | else:
316 | self.data[i]["value"]=self.frame[i].cget("text")
317 |
318 | self.values = []
319 | for i in range(self.rows):
320 | row_data = []
321 | for j in range(self.columns):
322 | row_data.append(self.data[i,j]["value"])
323 | self.values.append(row_data)
324 |
325 | def edit_row(self, row, value=None, **kwargs):
326 | """ edit all parameters of a single row """
327 | for i in range(self.columns):
328 | self.frame[row, i].configure(require_redraw=True, **kwargs)
329 | self.data[row, i]["args"].update(kwargs)
330 | if value is not None:
331 | self.insert(row, i, value)
332 | if (row,i) in self.corner_buttons.keys():
333 | self.dynamic_hover(self.corner_buttons[row,i],row,i)
334 | self.update_data()
335 |
336 | def edit_column(self, column, value=None, **kwargs):
337 | """ edit all parameters of a single column """
338 | for i in range(self.rows):
339 | self.frame[i, column].configure(require_redraw=True, **kwargs)
340 | self.data[i, column]["args"].update(kwargs)
341 | if value is not None:
342 | self.insert(i, column, value)
343 | if (i, column) in self.corner_buttons.keys():
344 | self.dynamic_hover(self.corner_buttons[i, column], i, column)
345 | self.update_data()
346 |
347 | def update_values(self, values, **kwargs):
348 | """ update all values at once """
349 | for i in self.frame.values():
350 | i.destroy()
351 | self.frame = {}
352 | self.values = values
353 | self.draw_table(**kwargs)
354 | self.update_data()
355 |
356 | def add_row(self, values, index=None, **kwargs):
357 | """ add a new row """
358 | for i in self.frame.values():
359 | i.destroy()
360 | self.frame = {}
361 | if index is None:
362 | index = len(self.values)
363 | try:
364 | self.values.insert(index, values)
365 | self.rows+=1
366 | except IndexError: pass
367 |
368 | self.draw_table(**kwargs)
369 | self.update_data()
370 |
371 | def add_column(self, values, index=None, **kwargs):
372 | """ add a new column """
373 | for i in self.frame.values():
374 | i.destroy()
375 | self.frame = {}
376 | if index is None:
377 | index = len(self.values[0])
378 | x = 0
379 | for i in self.values:
380 | try:
381 | i.insert(index, values[x])
382 | x+=1
383 | except IndexError: pass
384 | self.columns+=1
385 | self.draw_table(**kwargs)
386 | self.update_data()
387 |
388 | def delete_row(self, index=None):
389 | """ delete a particular row """
390 | if len(self.values)==1:
391 | return
392 | if index is None or index>=len(self.values):
393 | index = len(self.values)-1
394 | self.values.pop(index)
395 | for i in self.frame.values():
396 | i.destroy()
397 | self.rows-=1
398 | self.frame = {}
399 | self.draw_table()
400 | self.update_data()
401 |
402 |
403 | def delete_column(self, index=None):
404 | """ delete a particular column """
405 | if len(self.values[0])==1:
406 | return
407 | if index is None or index>=len(self.values[0]):
408 | try:
409 | index = len(self.values)-1
410 | except IndexError:
411 | return
412 | for i in self.values:
413 | i.pop(index)
414 | for i in self.frame.values():
415 | i.destroy()
416 | self.columns-=1
417 | self.frame = {}
418 | self.draw_table()
419 | self.update_data()
420 |
421 |
422 | def delete_rows(self, indices=[]):
423 | """ delete a particular row """
424 | if len(indices)==0:
425 | return
426 | self.values = [v for i, v in enumerate(self.values) if i not in indices]
427 | for i in indices:
428 | for j in range(self.columns):
429 | self.data[i, j]["args"] = ""
430 | for i in self.frame.values():
431 | i.destroy()
432 | self.rows -= len(set(indices))
433 | self.frame = {}
434 | self.draw_table()
435 | self.update_data()
436 |
437 | def delete_columns(self, indices=[]):
438 | """ delete a particular column """
439 | if len(indices)==0:
440 | return
441 | x = 0
442 |
443 | for k in self.values:
444 | self.values[x] = [v for i, v in enumerate(k) if i not in indices]
445 | x+=1
446 | for i in indices:
447 | for j in range(self.rows):
448 | self.data[j, i]["args"] = ""
449 |
450 | for i in self.frame.values():
451 | i.destroy()
452 | self.columns -= len(set(indices))
453 | self.frame = {}
454 | self.draw_table()
455 | self.update_data()
456 |
457 | def get_row(self, row):
458 | """ get values of one row """
459 | return self.values[row]
460 |
461 | def get_column(self, column):
462 | """ get values of one column """
463 | column_list = []
464 | for i in self.values:
465 | column_list.append(i[column])
466 | return column_list
467 |
468 | def select_row(self, row):
469 | """ select an entire row """
470 | self.edit_row(row, fg_color=self.hover_color)
471 | if self.orient!="horizontal":
472 | if self.header_color:
473 | self.edit_column(0, fg_color=self.header_color)
474 | else:
475 | if self.header_color:
476 | self.edit_row(0, fg_color=self.header_color)
477 | return self.get_row(row)
478 |
479 | def select_column(self, column):
480 | """ select an entire column """
481 | self.edit_column(column, fg_color=self.hover_color)
482 | if self.orient!="horizontal":
483 | if self.header_color:
484 | self.edit_column(0, fg_color=self.header_color)
485 | else:
486 | if self.header_color:
487 | self.edit_row(0, fg_color=self.header_color)
488 | return self.get_column(column)
489 |
490 | def deselect_row(self, row):
491 | """ deselect an entire row """
492 | self.edit_row(row, fg_color=self.fg_color if row%2==0 else self.fg_color2)
493 | if self.orient!="horizontal":
494 | if self.header_color:
495 | self.edit_column(0, fg_color=self.header_color)
496 | else:
497 | if self.header_color:
498 | self.edit_row(0, fg_color=self.header_color)
499 |
500 | def deselect_column(self, column):
501 | """ deselect an entire column """
502 | for i in range(self.rows):
503 | self.frame[i,column].configure(fg_color=self.fg_color if i%2==0 else self.fg_color2)
504 | if self.orient!="horizontal":
505 | if self.header_color:
506 | self.edit_column(0, fg_color=self.header_color)
507 | else:
508 | if self.header_color:
509 | self.edit_row(0, fg_color=self.header_color)
510 |
511 | def select(self, row, column):
512 | """ select any cell """
513 | if row == 0 and column == 0:
514 | hover_corners = ["", self.hover_color, self.hover_color, self.hover_color]
515 | elif row == self.rows - 1 and column == self.columns - 1:
516 | hover_corners = [self.hover_color, self.hover_color, "", self.hover_color]
517 | elif row == self.rows - 1 and column == 0:
518 | hover_corners=[self.hover_color, self.hover_color, self.hover_color, ""]
519 | elif row == 0 and column == self.columns - 1:
520 | hover_corners = [self.hover_color, "", self.hover_color, self.hover_color]
521 | else:
522 | hover_corners = [self.hover_color, self.hover_color, self.hover_color, self.hover_color]
523 | self.frame[row, column].configure(background_corner_colors=hover_corners, fg_color=self.hover_color)
524 |
525 | def deselect(self, row, column):
526 | """ deselect any cell """
527 | self.frame[row,column].configure(fg_color=self.fg_color if row%2==0 else self.fg_color2)
528 |
529 | def insert(self, row, column, value, **kwargs):
530 | """ insert value in a specific block [row, column] """
531 | if kwargs: self.data[row,column]["args"].update(kwargs)
532 | if self.write:
533 | self.frame[row,column].delete(0, customtkinter.END)
534 | self.frame[row,column].insert(0, value)
535 | self.frame[row,column].configure(**kwargs)
536 | else:
537 | self.frame[row,column].configure(require_redraw=True, text=value, **kwargs)
538 | if (row, column) in self.corner_buttons.keys():
539 | self.dynamic_hover(self.corner_buttons[row, column], row, column)
540 |
541 | self.update_data()
542 |
543 | def edit(self, row, column, **kwargs):
544 | """ change parameters of a cell without changing value """
545 | if kwargs: self.data[row,column]["args"].update(kwargs)
546 | if self.write:
547 | self.frame[row,column].configure(**kwargs)
548 | else:
549 | self.frame[row,column].configure(require_redraw=True, **kwargs)
550 | if (row, column) in self.corner_buttons.keys():
551 | self.dynamic_hover(self.corner_buttons[row, column], row, column)
552 |
553 | self.update_data()
554 |
555 | def delete(self, row, column, **kwargs):
556 | """ delete a value from a specific block [row, column] """
557 | if self.write:
558 | self.frame[row,column].delete(0, customtkinter.END)
559 | self.frame[row,column].configure(**kwargs)
560 | else:
561 | self.frame[row,column].configure(require_redraw=True, text="", **kwargs)
562 | if kwargs: self.data[row,column]["args"].update(kwargs)
563 | self.update_data()
564 |
565 | def get(self, row=None, column=None):
566 | """ get the required cell """
567 | if row is not None and column is not None:
568 | return self.data[row,column]["value"]
569 | else:
570 | return self.values
571 |
572 | def get_selected_row(self):
573 | """ Return the index and data of the selected row """
574 | selected_row_index = None
575 | for i in range(self.rows):
576 | if self.frame[i, 0].cget("fg_color") == self.hover_color:
577 | selected_row_index = i
578 | break
579 | selected_row_data = self.get_row(selected_row_index) if selected_row_index is not None else None
580 | return {"row_index": selected_row_index, "values": selected_row_data}
581 |
582 | def get_selected_column(self):
583 | """ Return the index and data of the selected row """
584 | selected_column_index = None
585 | for i in range(self.columns):
586 | if self.frame[0, i].cget("fg_color") == self.hover_color:
587 | selected_column_index = i
588 | break
589 | selected_column_data = self.get_column(selected_column_index) if selected_column_index is not None else None
590 | return {"column_index": selected_column_index, "values": selected_column_data}
591 |
592 | def configure(self, **kwargs):
593 | """ configure table widget attributes"""
594 |
595 | if "colors" in kwargs:
596 | self.colors = kwargs.pop("colors")
597 | self.fg_color = self.colors[0]
598 | self.fg_color2 = self.colors[1]
599 | if "fg_color" in kwargs:
600 | self.colors = (kwargs["fg_color"], kwargs.pop("fg_color"))
601 | self.fg_color = self.colors[0]
602 | self.fg_color2 = self.colors[1]
603 | if "bg_color" in kwargs:
604 | super().configure(bg_color=kwargs["bg_color"])
605 | self.inside_frame.configure(fg_color=kwargs["bg_color"])
606 | if "header_color" in kwargs:
607 | self.header_color = kwargs.pop("header_color")
608 | if "rows" in kwargs:
609 | self.rows = kwargs.pop("rows")
610 | if "columns" in kwargs:
611 | self.columns = kwargs.pop("columns")
612 | if "values" in kwargs:
613 | self.values = kwargs.pop("values")
614 | if "padx" in kwargs:
615 | self.padx = kwargs.pop("padx")
616 | if "pady" in kwargs:
617 | self.pady = kwargs.pop("pady")
618 | if "wraplength" in kwargs:
619 | self.wraplength = kwargs.pop("wraplength")
620 |
621 | for i in range(self.rows):
622 | for j in range(self.columns):
623 | self.data[i,j]["args"].update(kwargs)
624 |
625 | if "hover_color" in kwargs:
626 | self.hover_color = kwargs.pop("hover_color")
627 | if "text_color" in kwargs:
628 | self.text_color = kwargs.pop("text_color")
629 | if "border_width" in kwargs:
630 | self.border_width = kwargs.pop("border_width")
631 | super().configure(border_width=self.border_width)
632 | self.inside_frame.pack(expand=True, fill="both", padx=self.border_width, pady=self.border_width)
633 | if "border_color" in kwargs:
634 | self.border_color = kwargs.pop("border_color")
635 | super().configure(border_color=self.border_color)
636 | if "hover" in kwargs:
637 | self.hover = kwargs.pop("hover")
638 | if "anchor" in kwargs:
639 | self.anchor = kwargs.pop("anchor")
640 | if "corner_radius" in kwargs:
641 | self.corner = kwargs.pop("corner_radius")
642 | super().configure(corner_radius=self.corner)
643 | if "color_phase" in kwargs:
644 | self.phase = kwargs.pop("color_phase")
645 | if "justify" in kwargs:
646 | self.justify = kwargs.pop("justify")
647 | if "orientation" in kwargs:
648 | self.orient = kwargs.pop("orientation")
649 | if "write" in kwargs:
650 | self.write = kwargs.pop("write")
651 | if "width" in kwargs:
652 | self.width = kwargs.pop("width")
653 | if "height" in kwargs:
654 | self.height = kwargs.pop("height")
655 |
656 | self.update_values(self.values, **kwargs)
657 |
658 | def cget(self, param):
659 | if param=="width":
660 | return self.frame[0,0].winfo_reqwidth()
661 | if param=="height":
662 | return self.frame[0,0].winfo_reqheight()
663 | if param=="colors":
664 | return (self.fg_color, self.fg_color2)
665 | if param=="hover_color":
666 | return self.hover_color
667 | if param=="text_color":
668 | return self.text_color
669 | if param=="border_width":
670 | return self.border_width
671 | if param=="border_color":
672 | return self.border_color
673 | if param=="hover":
674 | return self.hover
675 | if param=="anchor":
676 | return self.anchor
677 | if param=="wraplength":
678 | return self.wraplength
679 | if param=="padx":
680 | return self.padx
681 | if param=="pady":
682 | return self.pady
683 | if param=="header_color":
684 | return self.header_color
685 | if param=="row":
686 | return self.rows
687 | if param=="column":
688 | return self.columns
689 | if param=="values":
690 | return self.values
691 | if param=="color_phase":
692 | return self.phase
693 | if param=="justify":
694 | return self.justify
695 | if param=="orientation":
696 | return self.orient
697 | if param=="write":
698 | return self.write
699 |
700 | return super().cget(param)
701 |
702 | def bind(self, sequence: str = None, command = None, add = True):
703 | """ bind all cells """
704 | self.binded_objects.append([sequence, command, add])
705 |
706 | super().bind(sequence, command, add)
707 | for i in self.frame:
708 | self.frame[i].bind(sequence, command, add)
709 | self.inside_frame.bind(sequence, command, add)
710 |
711 | def unbind(self, sequence: str = None, funcid: str = None):
712 | for i in self.binded_objects:
713 | if sequence in i:
714 | self.binded_objects.remove(i)
715 |
716 | super().unbind(sequence, funcid)
717 | for i in self.frame:
718 | self.frame[i].unbind(sequence, funcid)
719 | self.inside_frame.unbind(sequence, funcid)
720 |
--------------------------------------------------------------------------------