source: framspy/gui/widgets/listPopulationsWindow.py @ 1271

Last change on this file since 1271 was 1198, checked in by Maciej Komosinski, 22 months ago

Added simple Python GUI for Framsticks library/server

File size: 6.0 KB
RevLine 
[1198]1import tkinter as tk
2import tkinter.ttk as ttk
3from typing import Dict
4from gui.widgets.propertyWindow import PropertyWindow
5from gui.framsutils.FramsInterface import FramsInterface
6from functools import partial
7import operator
8from threading import Semaphore
9
10class 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)
Note: See TracBrowser for help on using the repository browser.