Skip to content

Commit

Permalink
Added a table, moved when events are initialized
Browse files Browse the repository at this point in the history
  • Loading branch information
Aesonus committed Dec 3, 2020
1 parent d59a0a9 commit bcdf93a
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 3 deletions.
78 changes: 78 additions & 0 deletions tests/test_get_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import pytest
from tklife.arrange import Autogrid

def test_grid_one_size_more_at_end():
"""
"""
expected = (
(0, 0),
(1, 0),
(0, 1),
(1, 1),
(0, 2),
(1, 2),
)
actual=Autogrid((1,)).grid_tuples(6)
assert tuple(actual) == expected

def test_grid_two_size():
"""
"""
expected = (
(0, 0), (1, 0), (2, 0), (3, 0),
(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1),
)
actual=Autogrid((2,3)).grid_tuples(10)
assert tuple(actual) == expected

def test_grid_two_size_more_at_end():
"""
"""
expected = (
(0, 0), (1, 0), (2, 0), (3, 0),
(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1),
(0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2),
)
actual=Autogrid((2,3)).grid_tuples(16)
assert tuple(actual) == expected

def test_grid_three_size_more_at_end():
"""
"""
expected = (
(0, 0), (1, 0), (2, 0), (3, 0),
(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1),
(0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), (7, 2),
(0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (5, 3), (6, 3), (7, 3)
)
actual= Autogrid((2,3,4)).grid_tuples(26)
assert tuple(actual) == expected

def test_grid_four_size_more_at_end():
"""
"""
expected = (
(0, 0), (1, 0), (2, 0), (3, 0),
(0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1),
(0, 2), (1, 2), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), (7, 2),
(0, 3), (1, 3), (2, 3), (3, 3),
(0, 4), (1, 4), (2, 4), (3, 4)
)
actual = Autogrid((2,3,4,2)).grid_tuples(26)
assert tuple(actual) == expected

def test_grid_dicts():
expected = (
{'a': 0, 'b':0}, {'a': 1, 'b':0},
{'a': 0, 'b':1}, {'a': 1, 'b':1},
)
actual = Autogrid((1, )).grid_dicts(4, 'ab')
assert tuple(actual) == expected

def test_zip_dicts():
expected = (
('elema', {'a': 0, 'b':0}), ('elemb', {'a': 1, 'b':0}),
('elemc', {'a': 0, 'b':1}), ('elemd', {'a': 1, 'b':1}),
)
actual = Autogrid((1, )).zip_dicts(('elema', 'elemb', 'elemc', 'elemd'), 'ab')
assert tuple(actual) == expected
4 changes: 2 additions & 2 deletions tklife/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ def __init__(self, **kwargs):
master = None
super().__init__(master, **kwargs)
self._create_vars()
self._create_events()
self._create_widgets()
self._create_events()
self._layout_widgets()

def _create_events(self):
"""Creates the events for the class"""

def _create_vars(self):
"""Creates variables for widgets"""

def _create_widgets(self):
"""Creates the widgets for the class"""

Expand Down
63 changes: 63 additions & 0 deletions tklife/arrange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Module for gridding out lists of elements"""
from math import floor
from itertools import accumulate

class Autogrid:
"""
Class for getting grid coordinates based on an enumeration
"""

def __init__(self, row_lengths, group_size=2):
"""
Sets the row lengths and group size for generators
"""
self.group_size = group_size
self.__divisors = tuple(
[length * group_size for length in row_lengths])
self.__row_boundaries = tuple(accumulate(self.__divisors))

def _get_row_index(self, index):
"""Gets the row that the index is in"""
for bound_index, boundary in enumerate(self.__row_boundaries):
if index < boundary:
return bound_index
return len(self.__row_boundaries) - 1

def _get_divisor(self, index):
return self.__divisors[self._get_row_index(index)]

def _get_x_offset(self, index):
row_index = self._get_row_index(index)
first = self.__row_boundaries[row_index]
return first % self._get_divisor(index)

def _get_y_offset(self, index):
row_index = self._get_row_index(index)
last = self.__row_boundaries[row_index]
if index < last:
return 0
return floor((index - last) / self._get_divisor(index)) + 1

def grid_tuples(self, element_count):
"""Yield grid coordinates as a 2-tuples (x, y)"""
for index in range(element_count):
items_per_row = self._get_divisor(index)
x_val = (index - self._get_x_offset(index)) % items_per_row
y_val = self._get_row_index(index) + self._get_y_offset(index)
yield (x_val, y_val)

def grid_dicts(self, element_count, keynames):
"""Yields grid coordinates as a dict using keynames as keys"""
if len(tuple(iter(keynames))) != self.group_size:
raise ValueError("'colnames' must be of length %s" %
(self.group_size))
return (
dict(zip(iter(keynames), coords))
for coords in self.grid_tuples(element_count)
)

def zip_dicts(self, elements, keynames):
length = len(elements)
return (
zip(elements, self.grid_dicts(length, keynames))
)
5 changes: 5 additions & 0 deletions tklife/behaviors.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ def undo_all(self, until=None):
while self.cursor != until:
self.undo()

def reset(self):
"""Clears the history completely"""
self.history.clear()
self.cursor = None

def _clear(self):
"""
Clears the history after the cursor
Expand Down
2 changes: 1 addition & 1 deletion tklife/simpledialog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from tklife import ModalDialog
from tkinter import StringVar, LEFT, END
from tkinter.ttk import Label, Entry, Button, Frame
from tkinter.messagebox import showwarning
from tklife import ModalDialog

class BaseSimpleDialog(ModalDialog):
"""
Expand Down
125 changes: 125 additions & 0 deletions tklife/widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""Creates some common widgets"""
from tkinter import Text, Canvas, X, VERTICAL, HORIZONTAL, LEFT, BOTTOM, RIGHT, Y, BOTH, END
from tkinter.ttk import Frame, Button, Scrollbar
from tklife import CommonFrame


class Table(CommonFrame):
sort_up = " ▲"
sort_down = " ▼"
col_pack_options = {
'fill': X,
'expand': True
}

def __init__(self, column_headers, data, **kwargs):
"""
Creates a table
"""
self.column_headers = column_headers
self.data = data
super().__init__(**kwargs)

def _create_events(self):
"""Create events"""
self.scrollable_canvas.bind(
"<Configure>",
lambda e: self.scrollable_canvas.configure(
scrollregion=self.scrollable_canvas.bbox("all")
)
)

def _create_vars(self):
"""Create widget variables"""

def _create_widgets(self):
"""Create widgets"""
self.table_frame = Frame(self)
self.scrollable_canvas = Canvas(self.table_frame)
self.x_scroll = Scrollbar(
self, orient=HORIZONTAL, command=self.scrollable_canvas.xview)
self.y_scroll = Scrollbar(
self.table_frame, orient=VERTICAL, command=self.scrollable_canvas.yview)
self.scrollable_canvas.configure(yscrollcommand=self.y_scroll.set,
xscrollcommand=self.x_scroll.set)
self.table = Frame(self.scrollable_canvas)
for header_text in self.column_headers:
widget = Frame(self.table)
button = Button(widget, text=header_text)
button.configure(
command=lambda button=button: self._sort_command(button))

self._create_data_widgets()

def _create_data_widgets(self):
for row in self.data:
for x_index, col_frame in enumerate(self.table.children.values()):
widget = Text(col_frame, width=20, height=1)
widget.insert('1.0', row[x_index])

def _layout_widgets(self):
"""Layout widgets"""
for col_frame in self.table.children.values():
for widget in col_frame.children.values():
widget.pack(**self.col_pack_options)
col_frame.pack(side=LEFT, fill=X, expand=True)
self.x_scroll.pack(side=BOTTOM, fill=X)
self.y_scroll.pack(side=RIGHT, fill=Y)
self.scrollable_canvas.pack(expand=True, fill=BOTH)
self.scrollable_canvas.create_window(
(0, 0), window=self.table, anchor="nw")
self.table_frame.pack(expand=True, fill=BOTH)

def _sort_command(self, button):
"""Event that sorts by the element"""
self.__reset_button_sort_text(except_button=button)
if self.sort_up in button['text']:
button.configure(text=button['text'][:-2] + self.sort_down)
elif self.sort_down in button['text']:
button.configure(text=button['text'][:-2] + self.sort_up)
else:
button.configure(text=button['text'] + self.sort_up)
column_data = [
tuple(enumerate(column.pack_slaves())) for column in self.table.children.values()
]
column_to_sort_by = [col for col in column_data if col[0][1] == button][0]
sort_kwargs = {
'key': self.__sort_key
}
if self.sort_down in button['text']:
sort_kwargs['reverse'] = True
sorted_column = sorted(column_to_sort_by[1:], **sort_kwargs)
self.__apply_sorting(sorted_column, column_data)

@staticmethod
def __sort_key(row):
text = row[1].get(1.0, END)
try:
return int(text)
except ValueError:
try:
return float(text)
except ValueError:
return text

def __apply_sorting(self, sorted_column, column_data):
for col in self.table.children.values():
for widget in tuple(col.children.values())[1:]:
widget.pack_forget()

index_order = [col[0] for col in sorted_column]
all_sorted_columns = []
for col in [data[1:] for data in column_data]:
all_sorted_columns.append([])
for index in index_order:
found = [t for t in col if t[0] == index][0]
all_sorted_columns[-1].append(found)
found[1].pack(**self.col_pack_options)
self.scrollable_canvas.update_idletasks()

def __reset_button_sort_text(self, except_button=None):
for col_widget in self.table.children.values():
button = tuple(col_widget.children.values())[0]
if button is not except_button:
button.configure(text=button['text'].replace(
self.sort_up, '').replace(self.sort_down, ''))

0 comments on commit bcdf93a

Please sign in to comment.