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

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

Added simple Python GUI for Framsticks library/server

File size: 6.0 KB
Line 
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.