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

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

Added simple Python GUI for Framsticks library/server

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