1 | import tkinter as tk
|
---|
2 | import tkinter.ttk as ttk
|
---|
3 | from typing import Dict
|
---|
4 | from gui.widgets.propertyWindow import PropertyWindow
|
---|
5 | from gui.framsutils.FramsInterface import FramsInterface
|
---|
6 | from functools import partial
|
---|
7 | import operator
|
---|
8 | from threading import Semaphore
|
---|
9 |
|
---|
10 | class ListPopulationsWindow(tk.Toplevel):
|
---|
11 | TREEVIEW_WIDTH = 600
|
---|
12 |
|
---|
13 | class FrameData:
|
---|
14 | def __init__(self, frame, treeview, prev, index, text) -> None:
|
---|
15 | self.frame = frame
|
---|
16 | self.treeview = treeview
|
---|
17 | self.prev = prev
|
---|
18 | self.index = index
|
---|
19 | self.text = text
|
---|
20 |
|
---|
21 | fields = ["name", "genotype", "energy", "lifespan", "uid"]
|
---|
22 | headers = ["Name", "Genotype", "Energy", "Lifespan"]
|
---|
23 | headers_width = [0.3, 0.5, 0.1, 0.1]
|
---|
24 |
|
---|
25 | def __init__(self, parent, posX, posY, height, refreshRate: int, framsSemaphore: Semaphore, frams: FramsInterface):
|
---|
26 | super().__init__(parent)
|
---|
27 | self.parent = parent
|
---|
28 | #self.transient(parent)
|
---|
29 | self.protocol("WM_DELETE_WINDOW", self._dismiss)
|
---|
30 | self.title("Populations")
|
---|
31 |
|
---|
32 | self.frams: FramsInterface = None
|
---|
33 | self.semaphore: Semaphore = framsSemaphore
|
---|
34 | self.frams = frams
|
---|
35 |
|
---|
36 | self.update()
|
---|
37 | self.height = height - self.winfo_rooty() + self.winfo_y()
|
---|
38 | self.posX = posX
|
---|
39 |
|
---|
40 | self.refreshRate = refreshRate
|
---|
41 |
|
---|
42 | #MAIN SECTION
|
---|
43 | frame_main = tk.Frame(master=self)
|
---|
44 | self.notebook_notebook = ttk.Notebook(frame_main)
|
---|
45 | self.frames: Dict[str, self.FrameData] = {}
|
---|
46 |
|
---|
47 | #WINDOW
|
---|
48 | self.notebook_notebook.grid(row=0, column=0, sticky="NSEW")
|
---|
49 | frame_main.columnconfigure(0, weight=1, minsize=0)
|
---|
50 | frame_main.rowconfigure(0, weight=1, minsize=0)
|
---|
51 | frame_main.grid(row=0, column=0, sticky="NSEW")
|
---|
52 |
|
---|
53 | self.columnconfigure(0, weight=1, minsize=self.TREEVIEW_WIDTH)
|
---|
54 | self.rowconfigure(0, weight=1, minsize=self.height)
|
---|
55 |
|
---|
56 | self.update()
|
---|
57 | width = self.winfo_width()
|
---|
58 | self.geometry("%dx%d+%d+%d" % (width, self.height, posX, posY))
|
---|
59 | #self.update()
|
---|
60 | self.refresh = False
|
---|
61 | self.dismissed = False
|
---|
62 | self.opened = True
|
---|
63 |
|
---|
64 | def clearList(self):
|
---|
65 | self.update()
|
---|
66 |
|
---|
67 | def refreshPopulations(self):
|
---|
68 | if not self.dismissed and self.frams:
|
---|
69 | self.semaphore.acquire()
|
---|
70 | populationsDict = self.frams.readPopulations(self.fields)
|
---|
71 | groupIndexes = self.frams.readPopulationsGroups()
|
---|
72 | self.semaphore.release()
|
---|
73 |
|
---|
74 | groups = set(populationsDict.keys())
|
---|
75 | groups_prev = set(self.frames.keys())
|
---|
76 | groups_remove = groups_prev - groups
|
---|
77 | for g in groups_remove:
|
---|
78 | f = self.frames.pop(g)
|
---|
79 | self.notebook_notebook.forget(f.frame)
|
---|
80 | groups_add = groups - groups_prev
|
---|
81 | for k, v in (sorted(groupIndexes.items(), key=lambda item: item[1])):
|
---|
82 | if k in groups_add:
|
---|
83 | self._addGroup(k, v)
|
---|
84 |
|
---|
85 | for (group, populations) in populationsDict.items():
|
---|
86 | if populations or (not populations and not self.frames[group].prev):
|
---|
87 | prevPop = set(self.frames[group].treeview.get_children())
|
---|
88 | popUids = {x["uid"] for x in populations}
|
---|
89 |
|
---|
90 | popToRemove = prevPop - popUids
|
---|
91 | popToAdd = popUids - prevPop
|
---|
92 | popToUpdate = popUids.intersection(prevPop)
|
---|
93 |
|
---|
94 | #first remove unwanted items to reduce time of further operations
|
---|
95 | for pop in popToRemove:
|
---|
96 | self.frames[group].treeview.delete(pop)
|
---|
97 |
|
---|
98 | #update remaining items
|
---|
99 | for pop in popToUpdate:
|
---|
100 | rec = next(x for x in populations if x["uid"] == pop)
|
---|
101 | self.frames[group].treeview.item(pop, text="{}-{}".format(rec["group"], rec["uid"]), values=self._makeValue(rec))
|
---|
102 |
|
---|
103 | #add new items
|
---|
104 | for pop in popToAdd:
|
---|
105 | rec = next(x for x in populations if x["uid"] == pop)
|
---|
106 | self.frames[group].treeview.insert(parent="", index="end", iid=rec["uid"], text="{}-{}".format(rec["group"], rec["uid"]), values=self._makeValue(rec))
|
---|
107 | self.frames[group].prev = len(populations)
|
---|
108 |
|
---|
109 | if self.refresh:
|
---|
110 | self.after(self.refreshRate + 1, self.refreshPopulations)
|
---|
111 |
|
---|
112 | def _makeValue(self, rec):
|
---|
113 | ret = []
|
---|
114 | for i, label in enumerate(self.fields):
|
---|
115 | if isinstance(rec[label], str):
|
---|
116 | ret.append(rec[label].replace('\n', "\\n"))
|
---|
117 | else:
|
---|
118 | ret.append(rec[label])
|
---|
119 | if i == len(self.headers) - 1:
|
---|
120 | break
|
---|
121 |
|
---|
122 | return tuple(ret)
|
---|
123 |
|
---|
124 | def _treeviewSelect(self, event):
|
---|
125 | itemId = event.widget.focus()
|
---|
126 | if itemId and event.widget.identify_row(event.y) == itemId:
|
---|
127 | item = event.widget.item(itemId)
|
---|
128 | if item:
|
---|
129 | group, uid = item["text"].split('-')
|
---|
130 | info = partial(self.frams.readCreatureDetails, group, uid)
|
---|
131 | update = partial(self.frams.writeCreatureDetail, uid)
|
---|
132 | cw = PropertyWindow(self, "Creature data", self.posX, info, update, self.frams.getError, self.frams, self.semaphore)
|
---|
133 |
|
---|
134 | def _dismiss(self):
|
---|
135 | self.dismissed = True
|
---|
136 | self.grab_release()
|
---|
137 | self.destroy()
|
---|
138 | self.opened = False
|
---|
139 |
|
---|
140 | def _addGroup(self, g, index):
|
---|
141 | masterFrame = tk.Frame(master=self)
|
---|
142 | masterFrame.columnconfigure(0, weight=1)
|
---|
143 | masterFrame.columnconfigure(1, weight=0)
|
---|
144 | masterFrame.rowconfigure(0, weight=1)
|
---|
145 |
|
---|
146 | treeview = ttk.Treeview(master=masterFrame, columns=tuple(self.headers), selectmode="browse")
|
---|
147 | scrollbar_treeview = ttk.Scrollbar(master=masterFrame, orient=tk.VERTICAL, command=treeview.yview)
|
---|
148 | treeview.configure(yscrollcommand=scrollbar_treeview.set)
|
---|
149 | treeview['show'] = 'headings'
|
---|
150 | for h, w in zip(self.headers, self.headers_width):
|
---|
151 | treeview.heading(h, text=h)
|
---|
152 | treeview.column(h, stretch=tk.YES, width=int(w*self.TREEVIEW_WIDTH))
|
---|
153 | treeview.bind("<Double-1>", self._treeviewSelect) #on double left click callback
|
---|
154 |
|
---|
155 | treeview.grid(row=0, column=0, sticky="NSEW")
|
---|
156 | scrollbar_treeview.grid(row=0, column=1, sticky="NSEW")
|
---|
157 | masterFrame.grid(row=0, column=0, sticky="NSEW")
|
---|
158 |
|
---|
159 | self.frames[g] = self.FrameData(masterFrame, treeview, 0, index, g)
|
---|
160 |
|
---|
161 | selected = self.notebook_notebook.select()
|
---|
162 |
|
---|
163 | self.notebook_notebook.add(self.frames[g].frame, text=g)
|
---|
164 |
|
---|
165 | #hide all tabs
|
---|
166 | for f in self.frames.values():
|
---|
167 | self.notebook_notebook.forget(f.frame)
|
---|
168 |
|
---|
169 | #show all tabs sorted
|
---|
170 | for frame in (sorted(self.frames.values(), key=operator.attrgetter("index"))):
|
---|
171 | self.notebook_notebook.add(frame.frame, text=frame.text)
|
---|
172 |
|
---|
173 | if selected:
|
---|
174 | self.notebook_notebook.select(selected)
|
---|
175 | else:
|
---|
176 | self.notebook_notebook.select(self.frames[g].frame) |
---|