1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
#coding=utf-8
import tkinter as tk
from tkinter import ttk
def getIntVal(widget, default=0):
try:
return int(widget.get().strip())
except ValueError:
return default
class WidgetRepeater(tk.Frame):
def __init__(self, master, widgetClass, headers=None, *args, **kwargs):
if not issubclass(widgetClass, RepeatableFrame):
raise AttributeError(
'WidgetRepeater widget must be a RepeatableFrame')
tk.Frame.__init__(self, master, **kwargs)
self.widgetClass = widgetClass
self.widgets = []
self.headers = headers
self.addButton = ttk.Button(
self, text='[+]', width=5, command=self._addWidget)
self.renderContent()
def _findWidget(self, row, column):
for children in self.children.values():
info = children.grid_info()
if info['row'] == str(row) and info['column'] == str(column):
return children
return None
def _addWidget(self):
removeButton = ttk.Button(
self, text='[-]', width=5,
command=lambda i=len(self.widgets): self._removeWidget(i))
removeButton.grid(row=len(self.widgets), column=0, sticky=tk.N)
widget = self.widgetClass(self)
self.widgets.append(widget)
self._updateGrid()
def _removeWidget(self, idx):
self.widgets.pop(idx).destroy()
self._findWidget(row=len(self.widgets), column=0).destroy()
self._updateGrid()
def _updateGrid(self):
for idx, widget in enumerate(self.widgets):
widget.grid(row=idx, column=1, sticky=tk.W+tk.E+tk.N+tk.S)
self.addButton.grid(
row=len(self.widgets), column=0, columnspan=1, sticky=tk.W+tk.N)
def _renderHeader(self):
if self.headers:
headerFrame = tk.Frame(self)
for idx, header in enumerate(self.headers):
headerFrame.columnconfigure(idx, weight=1)
widget = header[0](headerFrame, **header[1])
widget.grid(row=0, column=idx, sticky=tk.W+tk.E+tk.N)
self.widgets.append(headerFrame)
(tk.Label(self, text=' ')).grid(
row=0, column=0, sticky=tk.W+tk.E+tk.N)
def renderContent(self):
self.columnconfigure(1, weight=1)
self._renderHeader()
self._updateGrid()
def getValue(self):
return [widget.getValue() for widget in self.widgets
if isinstance(widget, self.widgetClass)]
class RepeatableFrame(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
self.renderContent()
def renderContent(self):
pass
def getValue(self):
pass
def setValue(self, value):
pass
class RepeatableEntry(RepeatableFrame):
def renderContent(self):
self.value = tk.StringVar()
self.field = ttk.Entry(self, textvariable=self.value)
self.field.pack(expand=True, fill=tk.BOTH)
def getValue(self):
return self.value.get()
def setValue(self, value):
return self.value.set(value)
class ScrollableFrame(tk.Frame):
def __init__(self, *args, **kwargs):
vertical = False
if 'vertical' in kwargs:
vertical = kwargs['vertical']
del kwargs['vertical']
horizontal = False
if 'horizontal' in kwargs:
horizontal = kwargs['horizontal']
del kwargs['horizontal']
tk.Frame.__init__(self, *args, **kwargs)
canvas = tk.Canvas(self, borderwidth=0, highlightthickness=0)
if horizontal:
hscroll = tk.Scrollbar(
self, orient=tk.HORIZONTAL, command=canvas.xview)
hscroll.pack(side=tk.BOTTOM, fill=tk.X)
canvas.configure(xscrollcommand=hscroll.set)
if vertical:
vscroll = tk.Scrollbar(
self, orient=tk.VERTICAL, command=canvas.yview)
vscroll.pack(side=tk.RIGHT, fill=tk.Y)
canvas.configure(yscrollcommand=vscroll.set)
frame = tk.Frame(canvas, borderwidth=0, highlightthickness=0)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
canvas.create_window((0,0), window=frame, anchor=tk.N+tk.W)
frame.bind(
'<Configure>',
lambda ev: canvas.configure(scrollregion=canvas.bbox('all')))
self.renderContent(frame)
def renderContent(self, container):
pass
|