source: framspy/gui/widgets/mainPage.py @ 1303

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

Introduced common utility functions for setting window sizes; untangled some repeated code

File size: 29.9 KB
Line 
1import tkinter as tk
2import tkinter.ttk as ttk
3from tkinter import StringVar, simpledialog, messagebox
4from gui.widgets.glFrame import AppOgl
5from typing import List
6from functools import partial
7from gui.framsutils.FramsInterface import TreeNode, InterfaceType
8from gui.widgets.mainTreeView import TreeView
9from gui.widgets.listGenePoolWindow import ListGenePoolWindow
10from gui.widgets.listPopulationsWindow import ListPopulationsWindow
11from gui.widgets.dialogBox import DirectoryDialgoBox, FileOpenDialogBox, FileSaveDialogBox
12from gui.widgets.ConsoleWindow import ConsoleWindow
13from gui.widgets.importWindow import ImportWindow
14from gui.widgets.propertyWindow import PropertyWindow
15from gui.libInterface import LibInterface
16from gui.socketInterface import SocketInterface
17from gui.utils import debounce
18from gui.utils import windowHideAndMaximize, windowShowAndSetGeometry
19from gui.widgets.ToolTip import CreateToolTip
20from time import perf_counter
21
22class MainPage(tk.Tk):
23    OPENGL_WIDTH = 720
24    OPENGL_HEIGHT = 480
25
26    SIDEBAR_WIDTH = 400
27    CONTROL_HEIGHT = 50
28    OPTIONS_WIDTH = 100
29    STATUSBAR_HEIGHT = 20
30
31    OFFSET_HEIGHT = 60
32
33    OPENGL_ANIMATE_DELAY = 1
34
35    MENU_CONNECT_TO_SERVER = "Connect to server"
36    MENU_CONNECT_TO_LIB = "Connect to library"
37
38    WORKAROUND_TKINTER_FREEZE_BUG = True # There is a bug in tkinter that freezes whole app when dialogs are called too fast, hint: https://stackoverflow.com/questions/40666956/tkinter-hangs-on-rapidly-repeated-dialog
39
40    refresh_rate_dict = {"0.1s": 100, "0.2s": 200, "0.5s": 500, "1s": 1000, "2s": 2000, "5s": 5000, "10s": 10000}
41
42    #paths which can reload world and tree
43    reload_path = ["/Experiment", "/Advanced scripting", "/World", "/User scripts"]
44
45    def __init__(self, parent, networkAddress: str = None, libPath: str = None):
46        super().__init__(parent)
47        self.parent = parent
48        self.protocol("WM_DELETE_WINDOW", self._dismiss)
49        self.title("Framsticks GUI for library/server")
50        self.option_add('*tearOff', tk.FALSE)
51
52        self.listRefreshRate = 1000
53        self.frams = None
54        self.canStep = False    #disable step while drawing
55
56        #OPENGL FRAME
57        self.frame_opengl = AppOgl(self, width=self.OPENGL_WIDTH, height=self.OPENGL_HEIGHT)
58        self.frame_opengl.animate = self.OPENGL_ANIMATE_DELAY
59        self.frame_opengl.bind("<Configure>", self.frame_opengl.onResize)
60        self.frame_opengl.bind("<Motion>", self.frame_opengl.onMouseMotion)
61        self.frame_opengl.bind("<MouseWheel>", self.frame_opengl.onScroll)
62        self.frame_opengl.bind("<Button>", self.frame_opengl.onMouseClick)
63        self.frame_opengl.bind("<ButtonRelease>", self.frame_opengl.onMouseRelease)
64        self.frame_opengl.bind("<Enter>", self.frame_opengl.onMouseEnter)
65
66        #SIDE FRAME
67        frame_sidebar = tk.Frame(master=self)
68        frame_sidebar.rowconfigure(0, weight=0)
69        frame_sidebar.rowconfigure(1, weight=1)
70        frame_sidebar.columnconfigure(0, weight=1)
71
72        ##CONTROL PANEL
73        frame_control_panel = tk.Frame(master=frame_sidebar, width=self.SIDEBAR_WIDTH, height=self.CONTROL_HEIGHT)
74        frame_control_panel.columnconfigure(0, weight=1, minsize=0)
75        frame_control_panel.columnconfigure(1, weight=1, minsize=0)
76        frame_control_panel.columnconfigure(2, weight=1, minsize=0)
77        frame_control_panel.columnconfigure(3, weight=1, minsize=0)
78        frame_control_panel.rowconfigure(0, weight=1, minsize=0)
79        frame_control_panel.grid_propagate(0)
80
81        frame_control_panel_combobox = tk.Frame(master=frame_control_panel, width=int(self.SIDEBAR_WIDTH/4))
82        frame_control_panel_combobox.rowconfigure(0, weight=1, minsize=0)
83        frame_control_panel_combobox.rowconfigure(1, weight=1, minsize=0)
84        frame_control_panel_combobox.columnconfigure(0, weight=1, minsize=0)
85        frame_control_panel_combobox.grid_propagate(0)
86        self.combobox_control_panel_fps = ttk.Combobox(master=frame_control_panel_combobox, state="readonly")
87        self.combobox_control_panel_fps.bind("<<ComboboxSelected>>", self.FPSCbCallback)
88        self.combobox_control_panel_refresh_rate = ttk.Combobox(master=frame_control_panel_combobox, values=list(self.refresh_rate_dict.keys()), state="readonly")
89        self.combobox_control_panel_refresh_rate.set(next(k for k, v in self.refresh_rate_dict.items() if v == self.listRefreshRate))
90        self.combobox_control_panel_refresh_rate.bind("<<ComboboxSelected>>", self.refreshRateCbCallback)
91        CreateToolTip(self.combobox_control_panel_fps, "Simulation steps to show")
92        CreateToolTip(self.combobox_control_panel_refresh_rate, "Refresh rate of gene pools and populations windows")
93
94        frame_control_panel_buttons = tk.Frame(master=frame_control_panel)
95        frame_control_panel_buttons.columnconfigure(0, weight=1, minsize=0)
96        frame_control_panel_buttons.columnconfigure(1, weight=1, minsize=0)
97        frame_control_panel_buttons.columnconfigure(2, weight=1, minsize=0)
98        frame_control_panel_buttons.rowconfigure(0, weight=1)
99        self.button_control_panel_start = tk.Button(master=frame_control_panel_buttons, text="start", command=self.controlPanelStartCommand)
100        self.button_control_panel_stop = tk.Button(master=frame_control_panel_buttons, text="stop", command=self.controlPanelStopCommand)
101        self.button_control_panel_step = tk.Button(master=frame_control_panel_buttons, text="step", command=self.controlPanelStepCommand)
102        self.button_control_panel_start["state"] = tk.DISABLED
103        self.button_control_panel_stop["state"] = tk.DISABLED
104        self.button_control_panel_step["state"] = tk.DISABLED
105        self.button_control_panel_start.grid(row=0, column=0, sticky="NSEW")
106        self.button_control_panel_stop.grid(row=0, column=1, sticky="NSEW")
107        self.button_control_panel_step.grid(row=0, column=2, sticky="NSEW")
108        self.combobox_control_panel_fps.grid(row=0, column=0, sticky="NSEW")
109        self.combobox_control_panel_refresh_rate.grid(row=1, column=0, sticky="NSEW")
110        frame_control_panel_combobox.grid(row=0, column=0, sticky="NSEW")
111        frame_control_panel_buttons.grid(row=0, column=1, columnspan=3, sticky="NSEW")
112        frame_control_panel.grid(row=0, column=0, sticky="NSEW")
113
114        ##TREEVIEW
115        frame_treeview = tk.Frame(master=frame_sidebar, width=self.SIDEBAR_WIDTH, height=self.OPENGL_HEIGHT - self.CONTROL_HEIGHT)
116        frame_treeview.columnconfigure(0, weight=1)
117        frame_treeview.columnconfigure(1, weight=0)
118        frame_treeview.rowconfigure(0, weight=1)
119        frame_treeview.rowconfigure(1, weight=0)
120
121        self.treeview_treeview = TreeView(master=frame_treeview, iconPath="gui/res/icons/", selectmode="browse")
122        scrollbar_treeview = ttk.Scrollbar(master=frame_treeview, orient=tk.VERTICAL, command=self.treeview_treeview.yview)
123        self.treeview_treeview.configure(yscrollcommand=scrollbar_treeview.set)
124        self.treeview_treeview.bind("<Double-1>", self.onTreeViewDoubleClick)
125        button_treeviewRefresh = tk.Button(master=frame_treeview, text="Refresh", command=self.refreshInfoTreeCommand)
126
127        self.treeview_treeview.grid(row=0, column=0, sticky="NSEW")
128        scrollbar_treeview.grid(row=0, column=1, sticky="NSEW")
129        button_treeviewRefresh.grid(row=1, column=0, sticky="NSEW")
130        frame_treeview.grid(row=1, column=0, sticky="NSEW")
131
132        #STATUS BAR
133        self.motd_text = StringVar("")
134        label_statusbar_motd = tk.Label(self, textvariable=self.motd_text, bd=1, height=1, relief=tk.SUNKEN, anchor=tk.W)
135
136        #MENU BAR
137        menu = tk.Menu(self)
138        self.menu_open = tk.Menu(menu, tearoff=0)
139        self.menu_open.add_command(label=self.MENU_CONNECT_TO_SERVER, command=self.menuConnectServerCommand)
140        self.menu_open.add_command(label=self.MENU_CONNECT_TO_LIB, command=self.menuConnectLibCommand)
141        self.menu_open.add_command(label="Disconnect", command=self.menuDisconnectCommand)
142        self.menu_open.entryconfig("Disconnect", state="disabled")
143        self.menu_open.add_separator()
144        self.menu_open.add_command(label="Exit", command=self.menuExitCommand)
145        menu.add_cascade(label="Main", menu=self.menu_open)
146        self.menu_file = tk.Menu(menu, tearoff=0)
147        self.menu_file.add_command(label="Load", command=self.menuFileLoadCommand, state="disabled")
148        self.menu_file.add_command(label="Import", command=self.menuFileImportCommand, state="disabled")
149        self.menu_file.add_command(label="Save experiment state as...", command=self.menuFileSaveESCommand, state="disabled")
150        self.menu_file.add_command(label="Save genotypes as...", command=self.menuFileSaveGCommand, state="disabled")
151        self.menu_file.add_command(label="Save simulator parameters as...", command=self.menuFileSaveSPCommand, state="disabled")
152        menu.add_cascade(label="File", menu=self.menu_file)
153        self.menu_options = tk.Menu(menu, tearoff=0)
154        self.menu_options.add_command(label="Console", command=self.menuConsoleCommand, state="disabled")
155        self.menu_options.add_command(label="Refresh world", command=self.refreshWorld, state="disabled")
156        self.enableColorsVar = tk.BooleanVar(value=False)
157        self.enableColorsVar.trace_add("write", self.menuEnableColorsCommand)
158        self.menu_options.add_checkbutton(label="Enable colors", onvalue=True, offvalue=False, variable=self.enableColorsVar)
159        self.menu_options.add_command(label="Restore windows", command=self.menuRestoreWindowsCommand)
160        menu.add_cascade(label="Options", menu=self.menu_options)
161        self.config(menu=menu)
162
163        #WINDOW
164        self.columnconfigure(0, weight=1)
165        self.columnconfigure(1, weight=0)
166        self.rowconfigure(0, weight=1)
167        self.rowconfigure(1, weight=0)
168        self.frame_opengl.grid(row=0, column=0, sticky="NSEW")
169        frame_sidebar.grid(row=0, column=1, sticky="NSEW")
170        label_statusbar_motd.grid(row=1, column=0, columnspan=2, sticky="NSEW")
171
172        #ORGANIZE WINDOWS POSITIONS
173        ## need to do some workaround to determine screen width and height
174        windowHideAndMaximize(self)
175        maxHeight = self.winfo_rooty() + self.winfo_height()
176        maxWidth = self.winfo_rootx() + self.winfo_width()
177        self.rootx = self.winfo_rootx()
178        windowShowAndSetGeometry(self, "%dx%d+%d+%d" % (self.OPENGL_WIDTH + self.SIDEBAR_WIDTH, self.OPENGL_HEIGHT, self.rootx, 0))
179        height = self.winfo_rooty() + self.winfo_height() - self.winfo_y()
180
181        self.list_gene_pool_window_height = int((maxHeight - height) / 2)
182        self.list_gene_pool_window_pos_y = height
183        self.list_gene_pool_window = ListGenePoolWindow(self, self.rootx, self.list_gene_pool_window_pos_y, self.list_gene_pool_window_height, self.listRefreshRate, self.frame_opengl.read_creature_semaphore, self.frams)
184        height2 = self.list_gene_pool_window.winfo_rooty() + self.list_gene_pool_window.winfo_height() - self.list_gene_pool_window.winfo_y()
185        self.list_populations_window_height = int((maxHeight - height) / 2)
186        self.list_populations_window_pos_y = height + height2
187        self.list_populations_window = ListPopulationsWindow(self, self.rootx, self.list_populations_window_pos_y, self.list_populations_window_height, self.listRefreshRate, self.frame_opengl.read_creature_semaphore, self.frams)
188
189        self.bind("<FocusIn>", self.on_focus_in)
190        self.bind("<FocusOut>", self.on_focus_out)
191
192        self.fps = 50
193        self.c_steps = 1
194
195        if networkAddress and not libPath:
196            self.menuConnectServerCommand(networkAddress)
197        elif libPath and not networkAddress:
198            self.menuConnectLibCommand(libPath)
199
200        self.sock_adr = "127.0.0.1:9009"
201        self.lib_adr = "D:\\Framsticks50rc25\\"
202
203        self.FPSCbCallback(None)
204
205        #step counter
206        self.nb_frames = 0
207        self.c_time = perf_counter()
208        self.sps = 0
209
210    def _dismiss(self):
211        """dismiss main window, close all connections, release all resources."""
212        if self.frams:
213            self.frams.disconnect()
214           
215        self.destroy()
216
217    def on_focus_in(self, event):
218        """restart rendering on focusing on main window."""
219        self.frame_opengl.animate = self.OPENGL_ANIMATE_DELAY
220        self.frame_opengl.tkExpose(None)
221
222    def on_focus_out(self, event):
223        """stop the rendering when main window lose focus."""
224        self.frame_opengl.animate = 0
225
226    def menuConnectServerCommand(self, adr: str = None):
227        """on "connect to server" button command."""
228        #if connection started from command argument
229        if adr:
230            address = adr
231        #else ask for server address
232        else:
233            address = simpledialog.askstring("Server address", "Address", parent=self, initialvalue=self.sock_adr)
234        if address:
235            ip, port = address.split(":")
236            try:
237                self.frams = SocketInterface()
238                self.frams.registerRunningChangeEventCallback(self.refreshControlPanelButtons)
239                self.frams.registerTreeviewRefreshEventCallback(self.refreshInfoTree)
240                self.frams.connect(ip, int(port)) #try to connect to server
241                self.sock_adr = address
242            except ConnectionError: #if connection cannot be established
243                messagebox.showerror(message="Cannot connect to server")
244                if self.frams:
245                    self.frams.disconnect()
246                self.frams = None
247       
248        #if connected successfully
249        if self.frams and self.frams.frams.comm.connected:
250            self._connect(address)
251
252    def menuConnectLibCommand(self, path: str = None):
253        """on "connect to library" button command."""
254        #if connection started from command argument
255        if path:
256            address = path
257        #else ask for library path
258        else:
259            address = DirectoryDialgoBox("Framsticks library path", "Path", parent=self, initialvalue=self.lib_adr)
260            address = address.result
261        if address:
262            try:
263                self.frams = LibInterface()
264                self.frame_opengl.onDraw = self.onDraw
265                self.prev_run = False
266                self.frams.connect(address, 0)
267                self.lib_adr = address
268            except ConnectionError:
269                messagebox.showerror(message="Cannot find Framsticks library")
270
271        self._connect(address)
272
273    def _connect(self, address):
274        """set all control's states if connected."""
275        if address and self.frams:
276            #set creatures read callback
277            self.frame_opengl.frams_readCreatures = self.frams.readCreatures
278            #prepare populations and gene pools windows
279            self.list_populations_window.frams = self.frams
280            self.list_gene_pool_window.frams = self.frams
281            self.list_populations_window.refresh = True
282            self.list_gene_pool_window.refresh = True
283            self.list_populations_window.refreshPopulations()
284            self.list_gene_pool_window.refreshGenePools()
285            #enable control buttons
286            self.button_control_panel_start["state"] = tk.NORMAL
287            self.button_control_panel_stop["state"] = tk.NORMAL
288            self.button_control_panel_step["state"] = tk.NORMAL
289            self.refreshInfoTree()
290            self.motd_text.set(self.frams.getMotd())
291            #setup all menus
292            self.menu_open.entryconfig("Disconnect", state="normal")
293            self.menu_open.entryconfig(self.MENU_CONNECT_TO_SERVER, state="disabled")
294            self.menu_open.entryconfig(self.MENU_CONNECT_TO_LIB, state="disabled")
295            self.menu_file.entryconfig("Load", state="normal")
296            self.menu_file.entryconfig("Import", state="normal")
297            self.menu_file.entryconfig("Save experiment state as...", state="normal")
298            self.menu_file.entryconfig("Save genotypes as...", state="normal")
299            self.menu_file.entryconfig("Save simulator parameters as...", state="normal")
300            self.menu_options.entryconfig("Refresh world", state="normal")
301
302            if self.frams.interfaceType == InterfaceType.SOCKET:
303                self.menu_options.entryconfig("Console", state="normal")
304            else:
305                self.menu_options.entryconfig("Console", state="disabled")
306
307            self.fps_values = self.frams.getFPSDefinitions()
308            def mapper(fps, step):
309                return "Every{}".format(", {} fps".format(fps) if fps > 0 else "") if step == 1 else "1:{}".format(step)
310            self.fps_mapvalues = [mapper(fps, step) for fps, step in self.fps_values]
311            init_val = mapper(self.fps, self.c_steps)
312            self.combobox_control_panel_fps['values'] = self.fps_mapvalues
313            self.combobox_control_panel_fps.set(init_val)
314
315            self.FPSCbCallback(None)
316
317            self.refreshWorld()
318            self.canStep = True     #enable step while drawing
319
320    def menuDisconnectCommand(self):
321        """set all control's states if disconnected."""
322        if self.frams:
323            self.canStep = False
324            self.frams.disconnect()
325            self.button_control_panel_start["state"] = tk.DISABLED
326            self.button_control_panel_stop["state"] = tk.DISABLED
327            self.button_control_panel_step["state"] = tk.DISABLED
328            self.list_populations_window.refresh = False
329            self.list_gene_pool_window.refresh = False 
330            self.list_populations_window.clearList()
331            self.list_gene_pool_window.clearList()
332            self.frame_opengl.frams_readCreatures = None
333            self.frame_opengl.onDraw = lambda: None
334            self.refreshInfoTree()
335            self.frams = None
336            self.frame_opengl.swap_buffer.clear()
337            self.motd_text.set("")
338            self.menu_open.entryconfig("Disconnect", state="disabled")
339            self.menu_open.entryconfig(self.MENU_CONNECT_TO_SERVER, state="normal")
340            self.menu_open.entryconfig(self.MENU_CONNECT_TO_LIB, state="normal")
341            self.menu_options.entryconfig("Console", state="disabled")
342            self.menu_file.entryconfig("Load", state="disabled")
343            self.menu_file.entryconfig("Import", state="disabled")
344            self.menu_file.entryconfig("Save experiment state as...", state="disabled")
345            self.menu_file.entryconfig("Save genotypes as...", state="disabled")
346            self.menu_file.entryconfig("Save simulator parameters as...", state="disabled")
347            self.menu_options.entryconfig("Refresh world", state="disabled")
348
349    def menuExitCommand(self):
350        self._dismiss()
351
352    def FPSCbCallback(self, event):
353        """handle fps change."""
354        value = self.combobox_control_panel_fps.get()
355        if value:
356            i = self.fps_mapvalues.index(value)
357            fps, step = self.fps_values[i]
358            self.fps = fps
359            self.c_steps = step
360
361            if fps == -1:
362                self.frame_opengl.animate = self.OPENGL_ANIMATE_DELAY = 1
363                self.frame_opengl.REFRESH_RATE = 0.001 #set to "as fast as possible" because how often redrawing is called is decided by the timer
364            else:
365                self.frame_opengl.animate = self.OPENGL_ANIMATE_DELAY = int(1000 / fps)
366                self.frame_opengl.REFRESH_RATE = 1 / fps
367
368    def refreshRateCbCallback(self, event):
369        """handle change of refresh rate of gene pools and populations windows."""
370        value = self.combobox_control_panel_refresh_rate.get()
371        if value:
372            self.listRefreshRate = self.refresh_rate_dict[value]
373            self.list_gene_pool_window.refreshRate = self.listRefreshRate
374            self.list_populations_window.refreshRate = self.listRefreshRate
375
376    def controlPanelStartCommand(self):
377        if self.frams:
378            self.frams.start()
379
380    def controlPanelStopCommand(self):
381        if self.frams:
382            self.frams.stop()
383            self.motd_text.set("")
384
385    def refreshControlPanelButtons(self, is_running: bool):
386        if is_running:
387            self.button_control_panel_start["state"] = tk.DISABLED
388            self.button_control_panel_stop["state"] = tk.NORMAL
389            self.button_control_panel_step["state"] = tk.DISABLED
390        else:
391            self.button_control_panel_start["state"] = tk.NORMAL
392            self.button_control_panel_stop["state"] = tk.DISABLED
393            self.button_control_panel_step["state"] = tk.NORMAL
394
395    def controlPanelStepCommand(self):
396        """hangle step button."""
397        if self.frams:  #if connected to frams
398            if self.frams.interfaceType == InterfaceType.LIB:
399                self.canStep = False    #disable step while drawing
400                run = self.frams.getSimulationStatus()
401                self.frams.start()
402                self.frams.step()
403                if not run:
404                    self.frams.stop()
405                self.canStep = True     #enable step while drawing
406            else:
407                self.canStep = False    #disable step while drawing
408                self.frams.step()
409                self.canStep = True     #enable step while drawing
410
411    def refreshInfoTreeCommand(self):
412        self.refreshInfoTree()
413        self.refreshWorld()
414
415    @debounce(1)
416    def refreshInfoTree(self):
417        """refresh info tree
418            debounce decorator prevents refreshing too often."""
419        self.treeview_treeview.delete(*self.treeview_treeview.get_children())
420        if self.frams:
421            tree: List[TreeNode] = self.frams.makeInfoTree()
422            self._recRefreshInfoTree(tree, True)
423
424    def _recRefreshInfoTree(self, node: TreeNode, open: bool = False):
425        #self.treeview_treeview.insert("" if not node.parent else node.parent.node.Id, index="end", iid=node.node.Id, text=node.node.Name)
426        self.treeview_treeview.insert(self._generateInfoTreeParent(node), index="end", iid=self._generateInfoTreeId(node), text=node.node.p["name"], ico=node.node.p["id"], open=open)
427        for child in node.children:
428            self._recRefreshInfoTree(child)
429
430    def _generateInfoTreeParent(self, node: TreeNode):
431        if not node.parent:
432            return ""
433        return self._generateInfoTreeId(node.parent)
434
435    def _generateInfoTreeId(self, node: TreeNode):
436        """generate unique id for info tree using paths and ids."""
437        response = node.node.p["id"]
438        while node.parent:
439            if node.parent:
440                if node.parent.node.p["id"] != '/':
441                    response = node.parent.node.p["id"] + "/" + response
442                else:
443                    response = node.parent.node.p["id"] + response
444                node = node.parent
445        return response
446
447    def onTreeViewDoubleClick(self, event):
448        if self.frams:
449            item = self.treeview_treeview.identify_row(event.y)
450            if item:
451                info = partial(self.frams.readParameterDetails, item)
452                update = partial(self.frams.writeParameterDetail, item)
453
454                if any(item.lower().endswith(x.lower()) for x in self.reload_path):
455                    pw = PropertyWindow(self, item, self.rootx, info, update, self.frams.getError, self.frams, self.frame_opengl.read_creature_semaphore, self.refreshWorld)
456                else:
457                    pw = PropertyWindow(self, item, self.rootx, info, update, self.frams.getError, self.frams, self.frame_opengl.read_creature_semaphore)
458                return 'break'
459
460    def onDraw(self):
461        """on draw callback, only for frams library."""
462        if self.frams:
463            run = self.frams.getSimulationStatus()
464            if run != self.prev_run:    #check if simulation status changed since last onDraw
465                self.refreshControlPanelButtons(run)    #if so, refresh command buttons
466                self.prev_run = run
467            if run:
468                if self.canStep: #if running and can step, perform N steps
469                    with self.frame_opengl.read_creature_semaphore:
470                        for i in range(self.c_steps):
471                            self.frams.step()
472
473                            #calculate steps per second
474                            c_time = perf_counter()
475                            self.nb_frames += 1
476                            e_time = c_time - self.c_time
477                            if e_time >= 1.0:
478                                self.sps = int(self.nb_frames / e_time)
479                                self.c_time = perf_counter()
480                                self.nb_frames = 0
481                                self.motd_text.set("{} steps/second".format(self.sps))
482            error = self.frams.getError()   #check for errors, not used anymore
483            if error:
484                messagebox.showerror("Framsticks error", error)
485
486    def menuConsoleCommand(self):
487        if self.frams.interfaceType == InterfaceType.SOCKET:
488            ConsoleWindow(self, self.frams.frams)
489        else:
490            self.menu_options.entryconfig("Console", state="disabled")
491
492    def menuFileLoadCommand(self):
493        if self.frams:
494            self.canStep = False
495            path = FileOpenDialogBox("Open File", "Path", parent=self, initialvalue=self.lib_adr, filetypes=[("Framsticks files", ['*.expt','*.gen','*.sim']), ("Experiment state", "*.expt"), ("Genotypes", "*.gen"), ("Simulator parameters", "*.sim"), ("All files", "*.*")])
496            path = path.result
497            if path:
498                #acquire read creature semaphore, so render thread could not ask for them, while they are changing
499                with self.frame_opengl.read_creature_semaphore:
500                    self.frams.loadFile(path)
501                self.refreshWorld()
502            self.canStep = True
503
504    def menuFileImportCommand(self):
505        if self.frams:
506            self.canStep = False
507            path = FileOpenDialogBox("Open File", "Path", parent=self, init_val=self.lib_adr, filetypes=[("Framsticks files", ['*.expt','*.gen','*.sim']), ("Experiment state", "*.expt"), ("Genotypes", "*.gen"), ("Simulator parameters", "*.sim"), ("All files", "*.*")])
508            path = path.result
509            if path:
510                #acquire read creature semaphore, so render thread could not ask for them, while they are changing
511                with self.frame_opengl.read_creature_semaphore:
512                    def handle_import_options():
513                            options = ImportWindow(parent=self, filename=path)
514                            options = options.result
515                            self.frams.importFile(path, options)
516                    if self.WORKAROUND_TKINTER_FREEZE_BUG:
517                        self.after(1, handle_import_options)
518                    else:
519                        handle_import_options()
520                   
521            self.canStep = True
522
523    def menuFileSaveESCommand(self):
524        if self.frams:
525            path = FileSaveDialogBox("Save experiment state to:", "Path", parent=self, initialvalue=self.lib_adr, filetypes=[("Experiment state", "*.expt")])
526            path = path.result
527            if path:
528                if path.split(".")[-1] != "expt":
529                    path += ".expt"
530                self.frams.saveFile(path, 1)
531
532    def menuFileSaveGCommand(self):
533        if self.frams:
534            path = FileSaveDialogBox("Save genotypes to:", "Path", parent=self, initialvalue=self.lib_adr, filetypes=[("Genotypes", "*.gen")])
535            path = path.result
536            if path:
537                if path.split(".")[-1] != "gen":
538                    path += ".gen"
539                self.frams.saveFile(path, 2)
540
541    def menuFileSaveSPCommand(self):
542        if self.frams:
543            path = FileSaveDialogBox("Save simulator parameters to:", "Path", parent=self, initialvalue=self.lib_adr, filetypes=[("Simulator parameters", "*.sim")])
544            path = path.result
545            if path:
546                if path.split(".")[-1] != "sim":
547                    path += ".sim"
548                self.frams.saveFile(path, 4)
549
550    def refreshWorld(self):
551        """refresh world parameters and shape."""
552        worldType = self.frams.getWorldType()
553        worldSize = self.frams.getWorldSize()
554        worldWaterLevel = self.frams.getWorldWaterLevel()
555        worldBoundaries = self.frams.getWorldBoundaries()
556        worldMap = self.frams.getWorldMap()
557        simType = self.frams.getSimtype()
558        self.frame_opengl.reloadWorld(worldType, simType, worldSize, worldMap, worldBoundaries, worldWaterLevel)
559
560    def menuEnableColorsCommand(self, *_):
561        v = self.enableColorsVar.get()
562        self.frame_opengl.frams_readCreatures_color = v
563
564    def menuRestoreWindowsCommand(self):
565        self.geometry("%dx%d+%d+%d" % (self.OPENGL_WIDTH + self.SIDEBAR_WIDTH, self.OPENGL_HEIGHT, self.rootx, 0))
566
567        if self.list_gene_pool_window.opened:
568            self.list_gene_pool_window._dismiss()
569        self.list_gene_pool_window = ListGenePoolWindow(self, self.rootx, self.list_gene_pool_window_pos_y, self.list_gene_pool_window_height, self.listRefreshRate, self.frame_opengl.read_creature_semaphore, self.frams)
570        if self.frams:
571            self.list_gene_pool_window.frams = self.frams
572            self.list_gene_pool_window.refresh = True
573            self.list_gene_pool_window.refreshGenePools()
574
575        if self.list_populations_window.opened:
576            self.list_populations_window._dismiss()
577        self.list_populations_window = ListPopulationsWindow(self, self.rootx, self.list_populations_window_pos_y, self.list_populations_window_height, self.listRefreshRate, self.frame_opengl.read_creature_semaphore, self.frams)
578        if self.frams:
579            self.list_populations_window.frams = self.frams
580            self.list_populations_window.refresh = True
581            self.list_populations_window.refreshPopulations()
Note: See TracBrowser for help on using the repository browser.