azapyGUI 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azapyGUI/AppSettingsPage.py +213 -0
- azapyGUI/AppSettingsPageMisc.py +101 -0
- azapyGUI/AppSettingsWindow.py +52 -0
- azapyGUI/BacktestComputation.py +166 -0
- azapyGUI/BacktestEntryWindow.py +307 -0
- azapyGUI/BacktestMenuPortfolioWindow.py +20 -0
- azapyGUI/CloneMenuPortfolioWindow.py +93 -0
- azapyGUI/CrossHairBCursor.py +80 -0
- azapyGUI/DF_Window.py +63 -0
- azapyGUI/DF_table.py +282 -0
- azapyGUI/EditMenuPortfolioWindow.py +16 -0
- azapyGUI/EditPortfolioWindow.py +475 -0
- azapyGUI/EntryClonePortfolioWindow.py +35 -0
- azapyGUI/EntryNameWindow.py +55 -0
- azapyGUI/EntryRenamePortfolioWindow.py +33 -0
- azapyGUI/GetMktData.py +85 -0
- azapyGUI/MenuApp.py +194 -0
- azapyGUI/MktDataFrame.py +129 -0
- azapyGUI/MktDataNode.py +34 -0
- azapyGUI/ModelParamEditWindow.py +143 -0
- azapyGUI/NrShares_table.py +54 -0
- azapyGUI/PortAnalyseWindow.py +179 -0
- azapyGUI/PortDataNode.py +180 -0
- azapyGUI/PortfolioFrame.py +197 -0
- azapyGUI/RebalanceMenuPortfolioWindow.py +21 -0
- azapyGUI/RemoveMenuPortfolioWindow.py +33 -0
- azapyGUI/SaveMenuPortfolioWindow.py +36 -0
- azapyGUI/Scrollable.py +60 -0
- azapyGUI/SelectOneWindow.py +65 -0
- azapyGUI/SymbAnalyseWindow.py +21 -0
- azapyGUI/SymbExtractWindow.py +129 -0
- azapyGUI/SymbTableEntry.py +109 -0
- azapyGUI/TimeSeriesViewWindow.py +480 -0
- azapyGUI/ViewTip.py +72 -0
- azapyGUI/WeightsWindow.py +352 -0
- azapyGUI/__init__.py +6 -0
- azapyGUI/azHelper.py +27 -0
- azapyGUI/azapyApp.py +89 -0
- azapyGUI/config.py +35 -0
- azapyGUI/configHelps.py +84 -0
- azapyGUI/configMSG.py +194 -0
- azapyGUI/configModels.py +519 -0
- azapyGUI/configPlot.py +70 -0
- azapyGUI/configSettings.py +138 -0
- azapyGUI/configTips.py +240 -0
- azapyGUI/mktDataValidation.py +42 -0
- azapyGUI/modelParametersValidation.py +442 -0
- azapyGUI/serviceMasterUserConfig.py +28 -0
- azapyGUI/tkHelper.py +18 -0
- azapyGUI-0.0.1.dist-info/LICENSE +674 -0
- azapyGUI-0.0.1.dist-info/METADATA +126 -0
- azapyGUI-0.0.1.dist-info/RECORD +54 -0
- azapyGUI-0.0.1.dist-info/WHEEL +5 -0
- azapyGUI-0.0.1.dist-info/top_level.txt +1 -0
azapyGUI/Scrollable.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Scrollable(tk.Frame):
|
|
6
|
+
def __init__(self, frame, sbwidth=16, canv_width=200, canv_height=200, **kwargs):
|
|
7
|
+
|
|
8
|
+
scrollbar = tk.Scrollbar(frame, width=sbwidth)
|
|
9
|
+
scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=False)
|
|
10
|
+
|
|
11
|
+
self._canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set,
|
|
12
|
+
width=canv_width, height=canv_height)
|
|
13
|
+
self._canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
14
|
+
|
|
15
|
+
scrollbar.config(command=self._canvas.yview)
|
|
16
|
+
self._canvas.bind('<Configure>', self._fill_canvas)
|
|
17
|
+
super().__init__(frame, **kwargs)
|
|
18
|
+
|
|
19
|
+
self.windows_item = self._canvas.create_window(0, 0, window=self, anchor=tk.NW)
|
|
20
|
+
|
|
21
|
+
self.bind('<Enter>', self._onEnter)
|
|
22
|
+
self.bind('<Leave>', self._onLeave)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _fill_canvas(self, event):
|
|
26
|
+
canvas_width = event.width
|
|
27
|
+
self._canvas.itemconfig(self.windows_item, width=canvas_width)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def update(self):
|
|
31
|
+
self.update_idletasks()
|
|
32
|
+
self._canvas.config(scrollregion=self._canvas.bbox(self.windows_item))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _onMouseWheel(self, event):
|
|
36
|
+
if platform.system() == 'Windows':
|
|
37
|
+
self._canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
|
|
38
|
+
elif platform.system() == 'Darwin':
|
|
39
|
+
self._canvas.yview_scroll(int(-1 * event.delta), "units")
|
|
40
|
+
else:
|
|
41
|
+
if event.num == 4:
|
|
42
|
+
self._canvas.yview_scroll(-1, "units")
|
|
43
|
+
elif event.num == 5:
|
|
44
|
+
self._canvas.yview_scroll(1, "units")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _onEnter(self, event):
|
|
48
|
+
if platform.system() == 'Linux':
|
|
49
|
+
self._canvas.bind_all("<Button-4>", self._onMouseWheel)
|
|
50
|
+
self._canvas.bind_all("<Button-5>", self._onMouseWheel)
|
|
51
|
+
else:
|
|
52
|
+
self._canvas.bind_all("<MouseWheel>", self._onMouseWheel)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _onLeave(self, event):
|
|
56
|
+
if platform.system() == 'Linux':
|
|
57
|
+
self._canvas.unbind_all("<Button-4>")
|
|
58
|
+
self._canvas.unbind_all("<Button-5>")
|
|
59
|
+
else:
|
|
60
|
+
self._canvas.unbind_all("<MouseWheel>")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
|
|
4
|
+
import azapyGUI.config as config
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SelectOneWindow:
|
|
8
|
+
def __init__(self, master=None, title=None, text=None, values=None,
|
|
9
|
+
tip_text=None, btn_text="Save"):
|
|
10
|
+
if (values is None) or (len(values) == 0): return
|
|
11
|
+
self._master = master
|
|
12
|
+
|
|
13
|
+
self._window = tk.Toplevel()
|
|
14
|
+
self._window.geometry("200x130")
|
|
15
|
+
self._window.title(title)
|
|
16
|
+
self._window.protocol("WM_DELETE_WINDOW", self._btn_cancel)
|
|
17
|
+
self._window.focus_set()
|
|
18
|
+
|
|
19
|
+
self.selection = None
|
|
20
|
+
|
|
21
|
+
frm = tk.LabelFrame(master=self._window, text=title, font=("Forte", 10))
|
|
22
|
+
frm.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
|
|
23
|
+
|
|
24
|
+
row = 0
|
|
25
|
+
lbl = tk.Label(master=frm, text=text)
|
|
26
|
+
lbl.grid(row=row, columnspan=2, pady=5, padx=5, sticky=tk.EW)
|
|
27
|
+
frm.rowconfigure(row, weight=1)
|
|
28
|
+
|
|
29
|
+
row += 1
|
|
30
|
+
self._ent = ttk.Combobox(master=frm, width=15, state='readonly')
|
|
31
|
+
self._ent['values'] = values
|
|
32
|
+
self._ent.current(0)
|
|
33
|
+
self._ent.grid(row=row, columnspan=2, padx=5, pady=5)
|
|
34
|
+
if tip_text is not None:
|
|
35
|
+
config.tiptil.bind(self._ent, tip_text)
|
|
36
|
+
frm.rowconfigure(row, weight=1)
|
|
37
|
+
|
|
38
|
+
row += 1
|
|
39
|
+
btn_calcel = tk.Button(master=frm, text="Cancel",
|
|
40
|
+
command=self._btn_cancel, width=8)
|
|
41
|
+
btn_calcel.grid(row=row, column=0, padx=5, pady=5, sticky=tk.W)
|
|
42
|
+
frm.columnconfigure(0, weight=1)
|
|
43
|
+
|
|
44
|
+
btn_action = tk.Button(master=frm, text=btn_text,
|
|
45
|
+
command=self._btn_action, width=8)
|
|
46
|
+
btn_action.grid(row=row, column=1, padx=5, pady=5, sticky=tk.E)
|
|
47
|
+
frm.columnconfigure(1, weight=1)
|
|
48
|
+
frm.rowconfigure(row, weight=1)
|
|
49
|
+
|
|
50
|
+
self._window.grab_set()
|
|
51
|
+
self._window.update()
|
|
52
|
+
if (self._master is not None) and self._master.winfo_exists():
|
|
53
|
+
self._master.wait_window(self._window)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _btn_cancel(self):
|
|
57
|
+
self._window.grab_release()
|
|
58
|
+
if (self._master is not None) and self._master.winfo_exists():
|
|
59
|
+
self._master.focus_set()
|
|
60
|
+
self._window.destroy()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _btn_action(self):
|
|
64
|
+
# to be implemented by the derived class
|
|
65
|
+
pass
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
|
|
3
|
+
import azapyGUI.config as config
|
|
4
|
+
from azapyGUI.TimeSeriesViewWindow import TimeSeriesViewWindow
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SymbAnalyseWindow(TimeSeriesViewWindow):
|
|
8
|
+
def __init__(self, master=None, symbols=None):
|
|
9
|
+
mktdata = {}
|
|
10
|
+
ref_name = symbols[0]
|
|
11
|
+
nnrr = config.MktDataDict[ref_name].mktdata.shape[0]
|
|
12
|
+
for symb in symbols:
|
|
13
|
+
mktdata[symb] = deepcopy(config.MktDataDict[symb].mktdata.drop(
|
|
14
|
+
columns=['symbol', 'divd', 'split', 'volume']))
|
|
15
|
+
if mktdata[symb].shape[0] > nnrr:
|
|
16
|
+
nnrr = mktdata[symb].shape[0]
|
|
17
|
+
ref_name = symb
|
|
18
|
+
col_name = 'close'
|
|
19
|
+
title = "Performance"
|
|
20
|
+
super().__init__(master=master, name=title, data=mktdata,
|
|
21
|
+
ref_name=ref_name, col_name=col_name)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import ttk
|
|
3
|
+
|
|
4
|
+
import azapyGUI.config as config
|
|
5
|
+
import azapyGUI.configTips as configTips
|
|
6
|
+
import azapyGUI.configSettings as configSettings
|
|
7
|
+
import azapyGUI.modelParametersValidation as mpv
|
|
8
|
+
from azapyGUI.GetMktData import GetMktData
|
|
9
|
+
from azapyGUI.mktDataValidation import sdate_edate_validate
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SymbExtractWindow:
|
|
13
|
+
def __init__(self, master=None, title=None, symbols=None, btn_text="OK", validate=False, entry=False):
|
|
14
|
+
self._master = master
|
|
15
|
+
self._symbols = symbols
|
|
16
|
+
self._validate = validate
|
|
17
|
+
self._entry = entry
|
|
18
|
+
|
|
19
|
+
self.errorSymb = []
|
|
20
|
+
|
|
21
|
+
self._window = tk.Toplevel()
|
|
22
|
+
self._window.title(title)
|
|
23
|
+
self._window.focus_set()
|
|
24
|
+
self._window.grab_set()
|
|
25
|
+
self._window.protocol("WM_DELETE_WINDOW", self._btn_cancel_func)
|
|
26
|
+
|
|
27
|
+
frm = tk.LabelFrame(master=self._window, text=title, font=("Forte", 10))
|
|
28
|
+
frm.pack(fill=tk.BOTH, expand=True)
|
|
29
|
+
|
|
30
|
+
for i in range(4): frm.columnconfigure(i, weight=1)
|
|
31
|
+
|
|
32
|
+
row = 0
|
|
33
|
+
frm.rowconfigure(row, weight=1)
|
|
34
|
+
if self._entry:
|
|
35
|
+
tk.Label(master=frm, text="Symbols").grid(row=row, column=0)
|
|
36
|
+
self._ent_symb = tk.Entry(master=frm)
|
|
37
|
+
self._ent_symb.grid(row=row, column=1, columnspan=3, pady=5, padx=5, sticky=tk.EW)
|
|
38
|
+
self._ent_symb.focus()
|
|
39
|
+
config.tiptil.bind(self._ent_symb, configTips._sew_symb_tip)
|
|
40
|
+
else:
|
|
41
|
+
text = mpv._list2string(self._symbols)
|
|
42
|
+
lbl = tk.Label(master=frm, text=text, justify=tk.LEFT)
|
|
43
|
+
lbl.grid(row=row, columnspan=4, pady=5, padx=5, sticky=tk.NSEW)
|
|
44
|
+
|
|
45
|
+
row += 1
|
|
46
|
+
frm.rowconfigure(row, weight=1)
|
|
47
|
+
lbl = tk.Label(master=frm, text="end date", anchor=tk.W)
|
|
48
|
+
lbl.grid(row=row, column=0, pady=5, padx=5, sticky=tk.EW)
|
|
49
|
+
|
|
50
|
+
self._ent_edate = tk.StringVar(master=frm, value='today')
|
|
51
|
+
ent_edate = tk.Entry(master=frm, width=12, validate='key', textvariable=self._ent_edate)
|
|
52
|
+
ent_edate['validatecommand'] = (ent_edate.register(mpv._validDateMMDDYYYY),'%S','%d','%P')
|
|
53
|
+
ent_edate.grid(row=row, column=1, pady=5, padx=5, sticky=tk.EW)
|
|
54
|
+
config.tiptil.bind(ent_edate, configTips._sew_edate_tip)
|
|
55
|
+
|
|
56
|
+
lbl = tk.Label(master=frm, text="Provider", anchor=tk.W)
|
|
57
|
+
lbl.grid(row=row, column=2, pady=5, sticky=tk.EW)
|
|
58
|
+
|
|
59
|
+
pval = list(configSettings.MasterApplicationSettings["Provider"].keys())
|
|
60
|
+
self._ent_provider = tk.StringVar(master=frm)
|
|
61
|
+
ent_provider = ttk.Combobox(master=frm, width=20, textvariable=self._ent_provider)
|
|
62
|
+
ent_provider["values"] = pval
|
|
63
|
+
ent_provider.current(0)
|
|
64
|
+
ent_provider.grid(row=row, column=3, pady=5, padx=5, sticky=tk.EW)
|
|
65
|
+
config.tiptil.bind(ent_provider, configTips._sew_provider_tip)
|
|
66
|
+
|
|
67
|
+
row += 1
|
|
68
|
+
frm.rowconfigure(row, weight=1)
|
|
69
|
+
lbl = tk.Label(master=frm, text="start date", anchor=tk.W)
|
|
70
|
+
lbl.grid(row=row, column=0, pady=5, padx=5, sticky=tk.EW)
|
|
71
|
+
|
|
72
|
+
self._ent_sdate = tk.StringVar(master=frm, value=None if validate else '1/1/2012')
|
|
73
|
+
ent_sdate = tk.Entry(master=frm, width=12, validate='key', textvariable=self._ent_sdate)
|
|
74
|
+
ent_sdate['validatecommand'] = (ent_sdate.register(mpv._validDateMMDDYYYY),'%S','%d','%P')
|
|
75
|
+
ent_sdate.grid(row=row, column=1, pady=5, padx=5, sticky=tk.EW)
|
|
76
|
+
config.tiptil.bind(ent_sdate, configTips._sew_sdate_tip)
|
|
77
|
+
|
|
78
|
+
frm_chk = tk.Frame(master=frm)
|
|
79
|
+
frm_chk.grid(row=row, column=3, pady=5, padx=5, sticky=tk.EW)
|
|
80
|
+
|
|
81
|
+
lbl = tk.Label(master=frm_chk, text="force", anchor=tk.W)
|
|
82
|
+
lbl.pack(padx=5, pady=5, side=tk.LEFT)
|
|
83
|
+
|
|
84
|
+
self._ent_force = tk.BooleanVar(value=False)
|
|
85
|
+
chk = tk.Checkbutton(master=frm_chk, onvalue=True, offvalue=False, variable=self._ent_force)
|
|
86
|
+
chk.pack(padx=5, pady=5, side=tk.RIGHT)
|
|
87
|
+
config.tiptil.bind(chk, configTips._sew_force_tip)
|
|
88
|
+
|
|
89
|
+
frm_btn = tk.Frame(master=self._window)
|
|
90
|
+
frm_btn.pack(fill=tk.BOTH, expand=True)
|
|
91
|
+
|
|
92
|
+
btn = tk.Button(master=frm_btn, text="Cancel", width=12, command=self._btn_cancel_func)
|
|
93
|
+
btn.pack(pady=5, padx=5, side=tk.LEFT)
|
|
94
|
+
|
|
95
|
+
btn = tk.Button(master=frm_btn, text=btn_text, width=12, command=self._btn_extract_func)
|
|
96
|
+
btn.pack(pady=5, padx=5, side=tk.RIGHT)
|
|
97
|
+
|
|
98
|
+
self._window.update()
|
|
99
|
+
self._master.wait_window(self._window)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _btn_cancel_func(self):
|
|
103
|
+
self._window.grab_release()
|
|
104
|
+
if (self._master is not None) and self._master.winfo_exists():
|
|
105
|
+
self._master.focus_set()
|
|
106
|
+
self._window.destroy()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _btn_extract_func(self):
|
|
110
|
+
if self._entry:
|
|
111
|
+
stat, self._symbols = mpv._validate_symbols(self._ent_symb.get().upper().split(','))
|
|
112
|
+
if len(self._symbols) < 1: return
|
|
113
|
+
|
|
114
|
+
source = self._ent_provider.get()
|
|
115
|
+
force = self._ent_force.get()
|
|
116
|
+
|
|
117
|
+
edate = self._ent_edate.get()
|
|
118
|
+
sdate = self._ent_sdate.get()
|
|
119
|
+
|
|
120
|
+
state, sd, ed = sdate_edate_validate(sdate, edate)
|
|
121
|
+
if not state:
|
|
122
|
+
tk.messagebox.showwarning("Warning", sd, parent=self._window)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
gmd = GetMktData(source, force, validate=self._validate)
|
|
126
|
+
gmd.getMkTDataSymb(self._symbols, sd, ed)
|
|
127
|
+
self.symbols = gmd.symbols
|
|
128
|
+
self.errorSymb = gmd.errorsymb
|
|
129
|
+
self._btn_cancel_func()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
|
|
3
|
+
import azapyGUI.config as config
|
|
4
|
+
from azapyGUI.Scrollable import Scrollable
|
|
5
|
+
from azapyGUI.modelParametersValidation import _validate_symbols
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SymbTableEntry:
|
|
9
|
+
def __init__(self, master, nrows=100, ncols=5, name=None, **kwargs):
|
|
10
|
+
self._name = name if name is not None else self._getName()
|
|
11
|
+
self._frm = tk.Frame(master=master)
|
|
12
|
+
self._nrows = nrows
|
|
13
|
+
self._ncols = ncols
|
|
14
|
+
|
|
15
|
+
self._stb = Scrollable(self._frm, **kwargs)
|
|
16
|
+
|
|
17
|
+
self._table = []
|
|
18
|
+
for i in range(self._nrows):
|
|
19
|
+
rw = []
|
|
20
|
+
for j in range(self._ncols):
|
|
21
|
+
self._stb.rowconfigure(i, weight=1)
|
|
22
|
+
self._stb.columnconfigure(j, weight=1)
|
|
23
|
+
ename = "!" + self._name + "_" + str(i) + "_" + str(j)
|
|
24
|
+
tt = tk.Entry(self._stb, width=5, name=ename)
|
|
25
|
+
tt.grid(row=i, column=j, sticky=tk.NSEW)
|
|
26
|
+
rw.append(tt)
|
|
27
|
+
self._table.append(rw)
|
|
28
|
+
|
|
29
|
+
self._table[0][0].focus_set()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def grid(self, *args, **kwargs):
|
|
33
|
+
self._frm.grid(*args, **kwargs)
|
|
34
|
+
self._stb.update()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def pack(self, *args, **kwargs):
|
|
38
|
+
self._frm.pack(*args, **kwargs)
|
|
39
|
+
self._stb.update()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get(self, row=None, column=None):
|
|
43
|
+
if (row is not None) and (column is not None):
|
|
44
|
+
return self._table[row][column]
|
|
45
|
+
|
|
46
|
+
tx = []
|
|
47
|
+
for i in range(self._nrows):
|
|
48
|
+
for j in range(self._ncols):
|
|
49
|
+
tt = self._table[i][j].get()
|
|
50
|
+
if len(tt) < 1: continue
|
|
51
|
+
tx.append(tt)
|
|
52
|
+
status, sout = _validate_symbols(tx)
|
|
53
|
+
return status, sorted(list(set(sout)))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _getName(self):
|
|
57
|
+
config.count_SymbTableEntry += 1
|
|
58
|
+
return 'SymbTableEntry' + str(config.count_SymbTableEntry)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def write_order(self, txt):
|
|
62
|
+
self.empty()
|
|
63
|
+
ic = 0
|
|
64
|
+
jc = 0
|
|
65
|
+
for tt in sorted(txt):
|
|
66
|
+
if tt == "": continue
|
|
67
|
+
self._table[ic][jc].insert(0, tt)
|
|
68
|
+
jc += 1
|
|
69
|
+
if jc >= self._ncols:
|
|
70
|
+
jc = 0
|
|
71
|
+
ic += 1
|
|
72
|
+
if ic >= self._nrows:
|
|
73
|
+
self._table[0][0].focus_set()
|
|
74
|
+
return
|
|
75
|
+
self._table[ic][jc].focus_set()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def empty(self):
|
|
79
|
+
for i in range(self._nrows):
|
|
80
|
+
for j in range(self._ncols):
|
|
81
|
+
self._table[i][j].delete(0, tk.END)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def table_focus_get(self):
|
|
85
|
+
tfx = str(self._table[0][0].focus_get())
|
|
86
|
+
fx = tfx.split("!")[-1].split("_")
|
|
87
|
+
if fx[0] == self._name:
|
|
88
|
+
return tuple(int(x) for x in fx[1:])
|
|
89
|
+
|
|
90
|
+
return (None, None)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def key_press(self, event):
|
|
94
|
+
i, j = self.table_focus_get()
|
|
95
|
+
|
|
96
|
+
if i is None: return
|
|
97
|
+
elif event.keysym == 'Up':
|
|
98
|
+
if i == 0: return
|
|
99
|
+
self._table[i-1][j].focus_set()
|
|
100
|
+
elif event.keysym == 'Down':
|
|
101
|
+
if i == self._nrows - 1: return
|
|
102
|
+
self._table[i+1][j].focus_set()
|
|
103
|
+
elif event.keysym == 'Return':
|
|
104
|
+
j += 1
|
|
105
|
+
if j == self._ncols:
|
|
106
|
+
j = 0
|
|
107
|
+
i += 1
|
|
108
|
+
if i == self._nrows: return
|
|
109
|
+
self._table[i][j].focus_set()
|