Evolife
Evolife has been developed to study Genetic algorithms, Natural evolution and behavioural ecology.
Evolife_Window.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2""" @brief Evolife Window system """
3
4#============================================================================#
5# EVOLIFE http://evolife.telecom-paris.fr Jean-Louis Dessalles #
6# Telecom Paris 2022-04-11 www.dessalles.fr #
7# -------------------------------------------------------------------------- #
8# License: Creative Commons BY-NC-SA #
9#============================================================================#
10# Documentation: https://evolife.telecom-paris.fr/Classes #
11#============================================================================#
12
13
14
15import sys
16if __name__ == '__main__': sys.path.append('../..') # for tests
17
18try: from PyQt5 import QtGui, QtCore, QtWidgets
19except ImportError: # compatibility with PyQt4
20 from PyQt4 import QtGui, QtCore
21 from PyQt4 import QtGui as QtWidgets
22
23import webbrowser # is user clicks on link
24import math
25import os.path
26import random
27
28from Evolife.Graphics import Plot_Area
29from Evolife.Graphics import Evolife_Graphic
30from Evolife.Graphics import Simulation_Thread # Thread to run the simulation in parallel
31from Evolife.Graphics import Screen # Physical screens
32from Evolife.Tools.Tools import EvolifeError
33
34DefaultIconName = 'Graphics/EvolifeIcon.png'
35HelpFileName = 'Help.txt'
36
37
38
39
40
43
45 """ Controls the simulation, either step by step, or in
46 a continuous mode.
47 """
48
49 def __init__(self, SimulationStep, Obs, method='timer'):
50 """ Stores Obs as observer
51 and SimulationStep as the function that processes one step of the simulation.
52 'method' can be 'timer' or 'thread' ('timer' preferred)
53 """
54 self.Obs = Obs # simulation observer
55 self.SimulationStep = SimulationStep # function that launches one step of the simulation
56 self.method = method # should be either 'timer' or 'thread'
57 self.timer = None # using a timer is one way of running simulation
58
59
60 self.simulation = None # name of the simulation thread
61 self.simulation_steady_mode = False # true when simulation is automatically repeated
62 self.simulation_under_way = True # becomes false when the simulation thinks it's really over
63 self.previous_Disp_period = self.Disp_period = Obs.DisplayPeriod() # display period
64
65 def RunButtonClick(self, event=None):
66 """ Entering in 'Run' mode
67 """
69 self.Obs.DisplayPeriod(self.Disp_period) # let Obs know
70 self.simulation_steady_mode = True # Continuous functioning
72
73 def StepButtonClick(self, event=None):
74 """ Entering in 'Step' mode
75 """
76 self.Disp_period = 1
77 self.Obs.DisplayPeriod(self.Disp_period) # let Obs know
78 self.simulation_steady_mode = False # Stepwise functioning
79 self.simulation_under_way = True # to allow for one more step
81
82 def Simulation_stop(self):
83 """ Stops the simulation thread or timer
84 """
85 if self.method == 'timer':
86 if self.timer is not None and self.timer.isActive():
87 self.timer.stop()
88 elif self.method == 'thread':
89 if self.simulation is not None:
90 self.simulation.stop()
91 if self.simulation.isAlive():
92 #print 'strange...'
93 self.simulation = None # well...
94 return False
95 self.simulation = None
96 return True
97
98 def Simulation_launch(self,continuous_mode):
99 """ (re)starts the simulation thread or timer
100 """
101 self.Simulation_stop()
102 if self.method == 'timer':
103 if continuous_mode:
104 if self.timer is None:
105 self.timer = QtCore.QTimer()
106 self.timer.timeout.connect(self.OneStep)
107 self.timer.start()
108 else:
109 self.OneStep()
110 elif self.method == 'thread':
111 # A new simulation thread is created
113 self.simulation.start()
114 return True
115
117 """ calls Simulation_launch
118 """
119 return self.Simulation_launch(self.simulation_steady_mode) # same functioning as before
120
121 def OneStep(self):
122 """ calls SimulationStep
123 """
124 # print('-', end="", flush=True)
125 if self.simulation_under_way:
126 try: self.simulation_under_way = self.SimulationStep()
127 except EvolifeError:
128 self.Simulation_stop()
129 import traceback
130 traceback.print_exc()
131 else:
132 self.StepButtonClick() # avoids to loop
133 self.DecisionToEnd()
134 if self.ReturnFromThread() < 0: # should return negative value only once, not next time
135 # if self.ReturnFromThread() < 0:
136 # The simulation is over
137 #self.Simulation_stop()
138 self.StepButtonClick()
139
141 """ to be overloaded
142 """
143 pass
144
145 def DecisionToEnd(self):
146 """ to be overloaded
147 """
148 pass
149
150
151
152
155
156
157Screen_ = None # to be instantiated in 'Start'
158
159#---------------------------#
160# Control panel #
161#---------------------------#
162
164 """ Minimal control panel with [Run] [Step] [Help] and [quit] buttons
165 """
166
167 def __init__(self, SimulationStep, Obs):
168 """ Creates a window with buttons that is also a Simulation_Control
169 """
170 self.Name = Obs.get_info('Title')
171 self.IconName = Obs.get_info('Icon')
172 if not self.IconName: self.IconName = DefaultIconName
173 Simulation_Control.__init__(self, SimulationStep, Obs, method='timer')
174 Evolife_Graphic.Active_Frame.__init__(self, parent=None, control=self)
175 if self.Name:
176 self.setWindowTitle(self.Name)
177 self.setWindowIcon(QtGui.QIcon(os.path.join(self.Obs.get_info('EvolifeMainDir'), self.IconName)))
178
179
180 self.SWindows = dict()
182 self.SWindowsMargins = dict()
183 self.Finish = False
184 self.alive = True
185 self.PhotoMode = 0 # no photo, no film
186 self.CurrentFrame = 0 # keeps track of photo numbers
187
188 # control frame
189 self.control_frame = QtWidgets.QVBoxLayout()
190 #self.control_frame.setGeometry(QtCore.QRect(0,0,60,100))
191
192 # inside control_frame we create two labels and button_frames
193 NameLabel = QtWidgets.QLabel("<font style='color:blue;font-size:17px;font-family:Comic Sans MS;font-weight:bold;'>%s</font>" % self.Name.upper(), self)
194 NameLabel.setAlignment(QtCore.Qt.AlignHCenter)
195 self.control_frame.addWidget(NameLabel)
196 AdrLabel = QtWidgets.QLabel("<a href=http://www.dessalles.fr/%s>www.dessalles.fr/%s</a>" % (self.Name.replace(' ','_'), self.Name), self)
197 AdrLabel.setAlignment(QtCore.Qt.AlignHCenter)
198 AdrLabel.linkActivated.connect(self.EvolifeWebSite)
199 self.control_frame.addWidget(AdrLabel)
200
201 # Button names
202 self.Buttons = dict()
203
204 # button frame
205 self.button_frame = QtWidgets.QVBoxLayout()
206 self.control_frame.addLayout(self.button_frame)
207
208 # Creating small button frame
209 self.SmallButtonFrame = QtWidgets.QHBoxLayout()
210 self.control_frame.addLayout(self.SmallButtonFrame)
211
212 # Creating help button frame
213 self.HelpButtonFrame = QtWidgets.QHBoxLayout()
214 self.control_frame.addLayout(self.HelpButtonFrame)
215
216 # Creating big buttons
217 self.Buttons['Run'] = self.LocalButton(self.button_frame, QtWidgets.QPushButton, "&Run", "Runs the simulation continuously", self.RunButtonClick) # Run button
218 self.Buttons['Step'] = self.LocalButton(self.button_frame, QtWidgets.QPushButton, "&Step", "Pauses the simulation or runs it stepwise", self.StepButtonClick)
219 self.control_frame.addStretch(1)
220 self.Buttons['Help'] = self.LocalButton(self.HelpButtonFrame, QtWidgets.QPushButton, "&Help", "Provides help about this interface", self.HelpButtonClick)
221 self.Buttons['Quit'] = self.LocalButton(self.control_frame, QtWidgets.QPushButton, "&Quit", "Quit the programme", self.QuitButtonClick)
222
223 # room for plot panel #
224 self.plot_frame = QtWidgets.QHBoxLayout()
225 self.plot_frame.addLayout(self.control_frame)
226 #self.plot_frame.addStretch(1)
227
228 self.setLayout(self.plot_frame)
229 self.setGeometry(*Screen_.locate(200, 200, 140, 300))
230 self.show()
231
232
233 def LocalButton(self, ParentFrame, ButtonType, Text, Tip, ClickFunction, ShortCutKey=None):
234 """ Creates a button
235 """
236 Button = ButtonType(Text, self)
237 Button.setToolTip(Tip)
238 Button.clicked.connect(ClickFunction)
239 if ShortCutKey is not None:
240 Button.setShortcut(QtGui.QKeySequence(ShortCutKey))
241 ParentFrame.addWidget(Button)
242 return Button
243
244 def EvolifeWebSite(self, e):
245 """ opens Web browser with provided address
246 """
247 webbrowser.open(e)
248
249 def HelpButtonClick(self, event=None):
250 """ Displays a text file named: Help.txt
251 """
252 if not 'Help' in self.SWindows:
253 self.SWindows['Help'] = Evolife_Graphic.Help_window(self)
254 self.SWindows['Help'].setWindowIcon(QtGui.QIcon(os.path.join(self.Obs.get_info('EvolifeMainDir'),self.IconName)))
255 try:
256 self.SWindows['Help'].display(os.path.join(self.Obs.get_info('EvolifeMainDir'), HelpFileName))
257 self.SWindows['Help'].setGeometry(*Screen_.locate(400, 120, 600, 500))
258
259 except IOError:
260 self.Obs.TextDisplay("Unable to find help file %s" % HelpFileName)
261 del self.SWindows['Help']
262 else: self.SWindows['Help'].Raise()
263
264 def QuitButtonClick(self, event):
265 """ closes the window
266 """
267 self.close()
268
270
271 def Raise(self):
272 """ puts the window in front of display
273 """
274 if self.isActiveWindow():
275 for SWName in self.SWindows:
276 self.SWindows[SWName].raise_()
277 if self.SWindows:
278 SWName = random.choice(list(self.SWindows.keys()))
279 self.SWindows[SWName].Raise()
280 else:
281 self.raise_()
282 self.activateWindow()
283
284
285 def closeEvent(self, event):
286 """ close satelite windows and stops the simulation
287 """
288 self.Finish = True
289 self.simulation_steady_mode = False # Stepwise functioning
290 for (SWName,SW) in list(self.SWindows.items()): # items() necessary here; list necessary for python 3
291 self.SWindows[SWName].close()
292 # No more satelite window left at this stage
293 self.Simulation_stop()
294 event.accept()
295
296 def SWDestroyed(self, SW):
297 """ A satellite window has been destroyed - removes it from the list
298 """
299 for SWName in self.SWindows:
300 if self.SWindows[SWName] == SW:
301 del self.SWindows[SWName]
302 return
303 error('Evolife_Window', 'Unidentified destroyed window')
304
306 """ Processes graphic orders if in visible state
307 returns -1 if Observer says the simulation is over
308 """
309 Simulation_Control.ReturnFromThread(self) # parent class procedure
310 if self.Obs.Visible(): self.Process_graph_orders()
311 if self.Obs.Over(): return -1 # Stops the simulation thread
312 return False
313
315 """ Just let Observer know that display has taken place
316 """
317 self.Obs.displayed() # Let Obs know that display takes place
318 self.CurrentFrame += 1
319 if self.PhotoMode == 1:
320 # single shot mode is over
321 self.PhotoMode = 0
322
323 def keyPressEvent(self, e):
324 """ processes actions such as Run, Step, Help...
325 """
326 if e.key() in [QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape]:
327 self.close()
328 elif e.key() in [QtCore.Qt.Key_S, QtCore.Qt.Key_Space]: # Space does not work...
329 self.StepButtonClick()
330 elif e.key() in [QtCore.Qt.Key_R, QtCore.Qt.Key_C]:
331 self.Buttons['Run'].animateClick()
332 elif e.key() in [QtCore.Qt.Key_H, QtCore.Qt.Key_F1]:
333 self.Buttons['Help'].animateClick()
334 elif e.key() in [QtCore.Qt.Key_M]: # to avoid recursion
335 self.Raise()
336 # let Obs know
337 try: self.Obs.inform(str(e.text()))
338 except UnicodeEncodeError: pass
339
340 def EventInterpreter(self, Event):
341 """ Sends event to observer (useful for mouse events)
342 """
343 # print(Event.EventType, (Event.EmittingClass, Event.Info))
344 self.Obs.recordInfo(Event.EventType, (Event.EmittingClass, Event.Info))
345 pass
346
347#---------------------------#
348# Control panel + Slider #
349#---------------------------#
351 """ This class combines a control panel and a slider for controlling display period
352 """
353
354 def __init__(self, SimulationStep, Obs, Background=None):
355 """ Create Control frame + displayperiod slider
356 """
357
358 Simulation_Control_Frame.__init__(self, SimulationStep, Obs)
359
360 # DisplayPeriod slider
361 self.lcd = QtWidgets.QLCDNumber(self)
362 self.lcd.SegmentStyle(QtWidgets.QLCDNumber.Filled)
363 lcdPalette = QtGui.QPalette()
364 lcdPalette.setColor(QtGui.QPalette.Light, QtGui.QColor(200,10,10))
365 self.lcd.setPalette(lcdPalette)
366 self.button_frame.addWidget(self.lcd)
367 self.DisplayPeriodSlider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self)
368 self.button_frame.addWidget(self.DisplayPeriodSlider)
369 self.DisplayPeriodSlider.valueChanged.connect(self.DisplayPeriodChanged)
370 self.DisplayPeriodSlider.setMinimum(0)
371 self.sliderPrecision = 5 # decimal precision, as now slider valueChanged events are integers
372 self.DisplayPeriodSlider.setMaximum(3 * 10 ** self.sliderPrecision)
373 self.DisplayPeriodSet(self.Obs.DisplayPeriod())
374
375 def DisplayPeriodChanged(self, event):
376 """ The displayed value varies exponentially with the slider's position
377 """
378 disp = int(10 ** ((int(event)+1)/(10.0 ** self.sliderPrecision)))
379 if (disp > 2999): disp = ((disp+500) // 1000) * 1000
380 elif (disp > 299): disp = ((disp+50) // 100) * 100
381 elif (disp > 29): disp = ((disp+5) // 10) * 10
382 elif (disp > 14): disp = ((disp+2) // 5) * 5
383 disp = int(disp)
386 self.lcd.display(str(disp))
387 self.Obs.DisplayPeriod(self.Disp_periodDisp_period) # let Obs know
388
389 def DisplayPeriodSet(self, Period, FlagForce=True):
390 if Period == 0: Period = 1
391 Position = int(math.log(abs(Period),10) * 10 ** self.sliderPrecision)
392 self.DisplayPeriodSlider.setSliderPosition(Position)
393 self.lcd.display(Period)
394
395
396
397#---------------------------#
398# Control panel + Curves #
399#---------------------------#
400
402 """ This class combines a control panel and a space to display curves
403 """
404
405 def __init__(self, SimulationStep, Obs, Background=None):
406 """ Creates a plot area and displays it to the right of the control frame
407 """
408
409 Simulation_Display_Control_Frame.__init__(self, SimulationStep, Obs)
410 self.setGeometry(*Screen_.locate(50, 50, 702, 420))
411
412
415 self.plot_area= Evolife_Graphic.AreaView(AreaType=Plot_Area.Plot_Area, image=Background, zoom=Screen_.ratio)
416 self.plot_frame.addWidget(self.plot_area,1)
417 #self.plot_area.show()
418 #self.plot_area.Area.drawPoints()
419 # self.Obs.TextDisplay(self.plot_area.Area.Curvenames(self.Obs.get_info('CurveNames')))
420
421 # adding legend button
422 self.Buttons['Legend'] = self.LocalButton(self.HelpButtonFrame, QtWidgets.QPushButton, "Legen&d", "Displays legend for curves", self.LegendButtonClick)
423
424
425 def LegendButtonClick(self, event=None):
426 """ Displays a text file named:
427 """
428 if not 'Legend' in self.SWindows:
429 self.SWindows['Legend'] = Evolife_Graphic.Legend_window(self)
430 self.SWindows['Legend'].setWindowIcon(QtGui.QIcon(os.path.join(self.Obs.get_info('EvolifeMainDir'),self.IconName)))
431 try:
432 self.plot_area.Area.Curvenames(self.Obs.get_info('CurveNames')) # stores curve names
433 Comments = self.Obs.get_info('WindowLegends')
434 # self.SWindows['Legend'].display(self.Obs.get_info('CurveNames'), Comments=Comments)
435 self.SWindows['Legend'].display(self.plot_area.Area.Legend(), Comments=Comments)
436 self.SWindows['Legend'].setGeometry(*Screen_.locate(50, 550, 600, 150))
437
438 except IOError:
439 self.Obs.TextDisplay("Unable to find information on curves")
440 del self.SWindows['Legend']
441 else:
442 # self.SWindows['Legend'].Raise()
443 self.SWindows['Legend'].close()
444 # del self.SWindows['Legend']
445
447 """ Processes graph orders received from observer.
448 Displays PlotData by adding points to curves and by displaying them.
449 """
450 if self.Finish: return
451 if self.PhotoModePhotoMode: # one takes a photo
452 ImgC = self.plot_area.photo('___Curves_', self.CurrentFrame, outputDir=self.Obs.get_info('OutputDir'))
453 if self.PhotoModePhotoMode == 1: # Photo mode, not film
454 self.Obs.TextDisplay('%s Created' % ImgC)
455 self.dump()
456 PlotData = self.Obs.get_info('PlotOrders')
457 if PlotData:
458 for (CurveId, Point) in PlotData:
459 self.plot_area.Area.plot(CurveId, Point, Width=3*Screen_.ratio)
460 Simulation_Control_Frame.Process_graph_orders(self)
461
462 def dump(self, verbose=False):
463 """ store and print simulation results
464 """
465 # creates a result file and writes parameter names into it
466 RF = self.Obs.get_info('ResultFile')
467 if RF:
468 self.plot_area.Area.Curvenames(self.Obs.get_info('CurveNames')) # stores curve names - may have been updated
469 AverageValues = self.plot_area.Area.dump(RF, self.Obs.get_info('ResultHeader'),
470 self.Obs.get_info('ResultOffset', 0))
471 if verbose:
472 self.Obs.TextDisplay('\n. ' + '\n. '.join(['%s\t%s' % (C, AverageValues[C]) for C in sorted(AverageValues)]))
473 self.Obs.TextDisplay('\nResults stored in %s*.csv' % os.path.normpath(RF))
474
475 def closeEvent(self, event):
476 """ close parent closeEvent
477 """
478 if self.alivealive: self.dump(verbose=True)
479 self.alivealive = False
480 Simulation_Control_Frame.closeEvent(self, event)
481 event.accept()
482
483#-------------------------------------------#
484# Control panel + Curves + Genomes + . . . #
485#-------------------------------------------#
486
488 """ Defines Evolife main window by modification of the generic Simulation Frame
489 """
490
491 def __init__(self, SimulationStep, Obs, Capabilities='C', Options=[]):
492 """ Creation of the main window and active satelite windows
493 """
494
497 self.Capabilities = list(Capabilities)
498 # Determining backagounds
499 self.Background = dict()
500 self.WindowTitles = dict()
501 self.DOptions = dict(Options)
502 self.Background['Default'] = "#F0B554"
503 if 'Background' in self.DOptions: # Default background for all windows
504 self.Background['Default'] = self.DOptions['Background']
505 if Obs.get_info('Background') is not None:
506 self.Background['Default'] = Obs.get_info('Background')
507 for W in ['Curves', 'Genomes', 'Photo', 'Trajectories', 'Network', 'Field', 'Log', 'Image']:
508 self.Background[W] = Obs.get_info(W + 'Wallpaper')
509 if self.Background[W] is None: self.Background[W] = self.Background['Default']
510 self.WindowTitles[W] = Obs.get_info(W + 'Title')
511 if self.WindowTitles[W] is None: self.WindowTitles[W] = W
512
513
514 if 'C' in self.Capabilities:
515 self.ParentClass = Simulation_Frame
516 Simulation_Frame.__init__(self, SimulationStep, Obs, Background=self.Background['Curves'])
517 elif set('FRGNT') & set(Capabilities):
518 self.ParentClass = Simulation_Display_Control_Frame
519 Simulation_Display_Control_Frame.__init__(self, SimulationStep, Obs)
520 else:
521 self.ParentClass = Simulation_Control_Frame
522 Simulation_Control_Frame.__init__(self, SimulationStep, Obs)
523
524
527
528 # Creating small buttons
529 if 'T' in self.Capabilities:
530 self.Buttons['Trajectories'] = self.LocalButton(self.SmallButtonFrame, QtWidgets.QCheckBox, "&T", 'Displays trajectories', self.TrajectoryButtonClick, QtCore.Qt.Key_T)
531 if 'N' in self.Capabilities:
532 self.Buttons['Network'] = self.LocalButton(self.SmallButtonFrame, QtWidgets.QCheckBox, "&N", 'Displays social links', self.NetworkButtonClick, QtCore.Qt.Key_N)
533 if set('FRI') & set(self.Capabilities):
534 # Region is a kind of field
535 self.Buttons['Field'] = self.LocalButton(self.SmallButtonFrame, QtWidgets.QCheckBox, "&F", 'Displays field', self.FieldButtonClick, QtCore.Qt.Key_F)
536 if 'L' in self.Capabilities:
537 self.Buttons['Log'] = self.LocalButton(self.SmallButtonFrame, QtWidgets.QCheckBox, "&L", 'Displays Labyrinth', self.LogButtonClick, QtCore.Qt.Key_L)
538
539 if 'R' in self.Capabilities: self.FieldOngoingDisplay = True
540 else: self.FieldOngoingDisplay = False
541
542 # Creating big buttons (they are big for historical reasons)
543 if 'G' in self.Capabilities:
544 self.Buttons['Genomes'] = self.LocalButton(self.button_frame, QtWidgets.QPushButton, "&Genomes", 'Displays genomes', self.GenomeButtonClick) # Genome button
545 if 'P' in self.Capabilities:
546 self.Buttons['Photo'] = self.LocalButton(self.button_frame, QtWidgets.QPushButton, "&Photo", 'Saves a .jpg picture', self.PhotoButtonClick) # Photo button
547
548 # Activate the main satellite windows
549 DefViews = self.Obs.get_info('DefaultViews')
550 if DefViews:
551 DefViews.reverse() # surprisingly necessary to get the last window active
552 for B in DefViews:
553 # two syntaxes allowed: 'WindowName' or ('Windowname', width [,height...])
554 if type(B) == str: self.Buttons[B].animateClick()
555 elif type(B) == tuple:
556 self.Buttons[B[0]].animateClick()
557 # self.Buttons[B[0]].animateClick(*B[1:])
558 if len(B) <= 3: # only dimensions provided
559 self.SWindowsPreferredGeometry[B[0]] = Screen_.resize(*B[1:])
560 else:
561 self.SWindowsPreferredGeometry[B[0]] = Screen_.locate(*B[1:5])
562 if len(B) > 4: # margins
563 self.SWindowsMargins[B[0]] = B[5:]
564 elif DefViews is None:
565 for B in ['Trajectories', 'Field', 'Network', 'Genomes', 'Log']: # ordered list
566 if B in self.Buttons:
567 self.Buttons[B].animateClick()
568 break # opening only one satelite window
569
570 # start mode
571 if 'Run' in self.DOptions and self.DOptions['Run'] in ['Yes', True]:
572 self.Buttons['Run'].animateClick()
573
574
575 def keyPressEvent(self, e):
576 """ recognizes shortcuts to show satelite windows (Genomes, Trajectories, Field, Legend, Film...)
577 """
578 self.ParentClass.keyPressEvent(self,e)
579 # Additional key actions
580 try:
581 if e.key() == QtCore.Qt.Key_G: self.Buttons['Genomes'].animateClick()
582 if e.key() == QtCore.Qt.Key_P: self.Buttons['Photo'].animateClick()
583 if e.key() == QtCore.Qt.Key_T: self.Buttons['Trajectories'].animateClick()
584 if e.key() == QtCore.Qt.Key_N: self.Buttons['Network'].animateClick()
585 if e.key() == QtCore.Qt.Key_F: self.Buttons['Field'].animateClick()
586 if e.key() == QtCore.Qt.Key_L: self.Buttons['Log'].animateClick()
587 if e.key() == QtCore.Qt.Key_I: self.Buttons['Image'].animateClick()
588 if e.key() == QtCore.Qt.Key_D: self.Buttons['Legend'].animateClick()
589 if e.key() == QtCore.Qt.Key_V: self.FilmButtonClick(e)
590 except KeyError: pass
591 self.checkButtonState()
592
593 def GenomeButtonClick(self, event):
594 if 'Genomes' not in self.Buttons: return
595 if not 'Genomes' in self.SWindows:
596 self.SWindows['Genomes'] = Evolife_Graphic.Genome_window(control=self,
597 outputDir=self.Obs.get_info('OutputDir'),
598 image=self.Background['Genomes'], zoom=Screen_.ratio)
599 # moving the window
600 self.SWindows['Genomes'].move(*Screen_.locate(800, 200))
601 self.WindowActivation('Genomes')
602 else: self.SWindows['Genomes'].Raise()
603
604 def PhotoButtonClick(self, event):
605 """ saves a snapshot of the simulation and goes to stepwise mode
606 """
607 if 'Photo' not in self.Buttons: return
609 self.Obs.TextDisplay('Photo mode ended\n')
611 else:
612 self.PhotoModePhotoModePhotoMode = 1 # take one shot
613 self.StepButtonClick()
614 self.Obs.TextDisplay('\nPhoto mode' + self.Obs.__str__() + '\n' + 'Frame %d' % self.CurrentFrame)
615 if not self.Obs.Visible(): self.Process_graph_ordersProcess_graph_orders() # possible if photo event occurs between years
616
617 def FilmButtonClick(self, event):
618 """ Film mode is activated by pressing the 'V' key (video)
619 It results in images (snapshots) being saved each time Observer is 'visible'
620 """
621 if 'Photo' not in self.Buttons: return
622 # at present, the button is not shown and is only accessible by pressing 'V'
625 self.setWindowTitle("%s (FILM MODE)" % self.Name)
626 else: self.setWindowTitle(self.Name)
627
628 def TrajectoryButtonClick(self, event):
629 """ displays the 'Trajectories' window
630 """
631 if 'Trajectories' not in self.Buttons: return
632 if 'Trajectories' not in self.SWindows:
633 self.SWindows['Trajectories'] = Evolife_Graphic.Trajectory_window(control=self,
634 Wtitle='Trajectories',
635 outputDir=self.Obs.get_info('OutputDir'),
636 image=self.Background['Trajectories'], zoom=Screen_.ratio)
637 # moving the window
638 self.SWindows['Trajectories'].move(*Screen_.locate(275, 500))
639 self.WindowActivation('Trajectories')
640 else: self.SWindows['Trajectories'].Raise()
641
642 def NetworkButtonClick(self, event):
643 """ displays the 'Network' window
644 """
645 if 'Network' not in self.Buttons: return
646 if 'Network' not in self.SWindows:
647 self.SWindows['Network'] = Evolife_Graphic.Network_window(control=self,
648 outputDir=self.Obs.get_info('OutputDir'),
649 image=self.Background['Network'], zoom=Screen_.ratio)
650 self.WindowActivation('Network')
651 self.SWindows['Network'].move(*Screen_.locate(790, 500))
652 else: self.SWindows['Network'].Raise()
653
654 def FieldButtonClick(self, event):
655 """ displays the 'Field' window
656 """
657 if 'Field' not in self.Buttons: return
658 if 'Field' not in self.SWindows:
659 self.SWindows['Field'] = Evolife_Graphic.Field_window(control=self,
660 Wtitle=self.Name,
661 outputDir=self.Obs.get_info('OutputDir'),
662 image=self.Background['Field'], zoom=Screen_.ratio)
663 # moving the window
664 self.SWindows['Field'].move(*Screen_.locate(800, 100))
665 self.WindowActivation('Field')
666 else: self.SWindows['Field'].Raise()
667
668 def LogButtonClick(self, event):
669 """ [not implemented]
670 """
671 if 'Log' not in self.Buttons: return
672 self.Obs.TextDisplay('LogTerminal\n')
673 pass
674
675 def WindowActivation(self, WindowName): # complement after click
676 """ Sets Satellite window's geometry, icon and title
677 """
678 self.SWindows[WindowName].setWindowIcon(QtGui.QIcon(os.path.join(self.Obs.get_info('EvolifeMainDir'),self.IconName)))
680 if WindowName in self.SWindowsPreferredGeometry:
681 self.SWindows[WindowName].dimension(*self.SWindowsPreferredGeometry[WindowName])
682 if WindowName in self.SWindowsMargins and self.SWindowsMargins[WindowName]:
683 self.SWindows[WindowName].Area.set_margins(*self.SWindowsMargins[WindowName])
684 if WindowName in self.WindowTitles: self.SWindows[WindowName].setWindowTitle(self.WindowTitles[WindowName])
685
687 """ sets the availability of the 'Network', 'Field', 'Image', 'Trajectories' tick button on display
688 """
689 for B in self.Buttons:
690 if B in ['Network', 'Field', 'Image', 'Trajectories', 'Log']:
691 if self.Buttons[B].isEnabled and B not in self.SWindows:
692 self.Buttons[B].setCheckState(False)
693 if self.Buttons[B].isEnabled and B in self.SWindows:
694 self.Buttons[B].setCheckState(True)
695
697 """ Processes graph orders received from observer.
698 Parent class displays PlotData by adding points to curves and by displaying them.
699 In addition, gets orders for satelite windows from Oberver and processes them.
700 """
701
702 ImgG, ImgN, ImgF, ImgT = ('',) * 4
703 if 'Genomes' in self.SWindows:
704 ImgG = self.SWindows['Genomes'].genome_display(genome=self.Obs.get_data('DNA'),
705 gene_pattern=self.Obs.get_info('GenePattern'),
706 Photo=self.PhotoModePhotoModePhotoMode, CurrentFrame=self.CurrentFrame)
707 if 'Network' in self.SWindows:
708 ImgN = self.SWindows['Network'].Network_display(self.Obs.get_data('Field', Consumption=False),
709 self.Obs.get_data('Network'),
710 Photo=self.PhotoModePhotoModePhotoMode, CurrentFrame=self.CurrentFrame)
711 if 'Field' in self.SWindows:
712 self.SWindows['Field'].image_display(self.Obs.get_info('Image'), windowResize=True)
713 ImgF = self.SWindows['Field'].Field_display(self.Obs.get_data('Field'),
715 CurrentFrame=self.CurrentFrame,
716 Ongoing=self.FieldOngoingDisplay, Prefix='___Field_')
717 if 'Trajectories' in self.SWindows:
718 self.SWindows['Trajectories'].image_display(self.Obs.get_info('Pattern'), windowResize=True)
719 ImgT = self.SWindows['Trajectories'].Field_display(self.Obs.get_data('Trajectories'),
721 CurrentFrame=self.CurrentFrame, Ongoing=self.FieldOngoingDisplay, Prefix='___Traj_')
722 if self.PhotoModePhotoModePhotoMode == 1:
723 if ''.join([ImgG, ImgN, ImgF, ImgT]):
724 self.Obs.TextDisplay('%s Created' % ' '.join([ImgG, ImgN, ImgF, ImgT]))
725 self.ParentClass.Process_graph_orders(self) # draws curves (or not)
726 self.checkButtonState()
727
728 def DecisionToEnd(self):
729 """ Exits if 'ExitOnEnd' is True
730 """
731 if 'ExitOnEnd' in self.DOptions and self.DOptions['ExitOnEnd'] in [True, 'Yes']:
732 self.PhotoModePhotoModePhotoMode = 1 # taking photos
734 self.Buttons['Quit'].animateClick() # exiting
735
736 def SWDestroyed(self, SW):
737 self.ParentClass.SWDestroyed(self,SW)
738 self.checkButtonState()
739
740 def closeEvent(self, event):
741 self.ParentClass.closeEvent(self, event)
742 event.accept()
743
744
745
748
749def Start(SimulationStep, Obs, Capabilities='C', Options={}):
750 """ SimulationStep is a function that performs a simulation step
751 Obs is the observer that stores statistics
752 Capabilities (curves, genome display, trajectory display...)
753 = any string of letters from: CFGNTP
754 C = Curves
755 F = Field (2D seasonal display) (excludes R)
756 G = Genome display
757 I = Image (same as Field, but no slider)
758 L = Log Terminal
759 N = social network display
760 P = Photo (screenshot)
761 R = Region (2D ongoing display) (excludes F)
762 T = Trajectory display
763
764 Options is a dict:
765 - Run:True means that the simulation will run automatically
766 - Background:<Colour or image>
767 - ExitOnEnd:True doesn't pause when simulation stops
768
769 """
770
771 MainApp = QtWidgets.QApplication(sys.argv)
772
773 global Screen_
774 Screen_ = Screen.Screen_(MainApp) # stores physical dimensions of display
775 Screen_.switchScreen()
776
777 if set(Capabilities) <= set('CFGILNPRT'):
778 # print(Evolife_Frame.__mro__)
779 MainWindow = Evolife_Frame(SimulationStep, Obs, Capabilities, Options)
780
781 # Entering main loop
782 MainApp.exec_()
783 # if os.name != 'nt':
784 MainApp.deleteLater() # Necessary to avoid problems on Unix
785 else:
786 MainWindow = None
787 print(""" Error: <Capabilities> should be a string of letters taken from:
788 C = Curves
789 F = Field (2D seasonal display) (excludes R)
790 G = Genome display
791 I = Image (same as Field, but no slider)
792 L = Log Terminal
793 N = social network display
794 P = Photo (screenshot)
795 R = Region (2D ongoing display) (excludes F)
796 T = Trajectory display
797 """)
798
799
800
801
802if __name__ == '__main__':
803
804 print(__doc__)
805
806
807__author__ = 'Dessalles'
An Active_frame reacts to basic keyboard control.
def Raise(self)
puts the window on top of display
Standard canvas plus resizing capabilities.
def photo(self, Name, FrameNumber=-1, outputDir='.', extension='png')
takes a snapshot and saves it to a new file
A graphic area that displays moving agents #.
Genome_window: An image area that displays binary genomes.
Displays a text file supposed to provide help.
Network_window: A drawing area that displays social links The population is displayed twice,...
Defines Evolife main window by modification of the generic Simulation Frame.
def __init__(self, SimulationStep, Obs, Capabilities='C', Options=[])
Creation of the main window and active satelite windows.
def TrajectoryButtonClick(self, event)
displays the 'Trajectories' window
def NetworkButtonClick(self, event)
displays the 'Network' window
def PhotoButtonClick(self, event)
saves a snapshot of the simulation and goes to stepwise mode
def FieldButtonClick(self, event)
displays the 'Field' window
def WindowActivation(self, WindowName)
Sets Satellite window's geometry, icon and title.
def keyPressEvent(self, e)
recognizes shortcuts to show satelite windows (Genomes, Trajectories, Field, Legend,...
def DecisionToEnd(self)
Exits if 'ExitOnEnd' is True.
def FilmButtonClick(self, event)
Film mode is activated by pressing the 'V' key (video) It results in images (snapshots) being saved e...
def LogButtonClick(self, event)
[not implemented]
def Process_graph_orders(self)
Processes graph orders received from observer.
def checkButtonState(self)
sets the availability of the 'Network', 'Field', 'Image', 'Trajectories' tick button on display
Capabilities
Creation of the main window #.
def closeEvent(self, event)
close parent closeEvent
Minimal control panel with [Run] [Step] [Help] and [quit] buttons.
def __init__(self, SimulationStep, Obs)
Creates a window with buttons that is also a Simulation_Control.
def LocalButton(self, ParentFrame, ButtonType, Text, Tip, ClickFunction, ShortCutKey=None)
Creates a button.
def HelpButtonClick(self, event=None)
Displays a text file named: Help.txt.
def EvolifeWebSite(self, e)
opens Web browser with provided address
Interface with the simulation thread #.
def Simulation_resume(self)
calls Simulation_launch
def StepButtonClick(self, event=None)
Entering in 'Step' mode.
def RunButtonClick(self, event=None)
Entering in 'Run' mode.
simulation
Status of the simulation programme.
def Simulation_launch(self, continuous_mode)
(re)starts the simulation thread or timer
def Simulation_stop(self)
Stops the simulation thread or timer.
This class combines a control panel and a slider for controlling display period.
def __init__(self, SimulationStep, Obs, Background=None)
Create Control frame + displayperiod slider.
def DisplayPeriodChanged(self, event)
The displayed value varies exponentially with the slider's position.
This class combines a control panel and a space to display curves.
def dump(self, verbose=False)
store and print simulation results
def __init__(self, SimulationStep, Obs, Background=None)
Creates a plot area and displays it to the right of the control frame.
def LegendButtonClick(self, event=None)
Displays a text file named:
def closeEvent(self, event)
close parent closeEvent
def Process_graph_orders(self)
Processes graph orders received from observer.
Graphic area for displaying curves #.
Definition: Plot_Area.py:384
def display(self)
calling 'display' for all individuals in the population
Definition: Population.py:200
def Start(SimulationStep, Obs, Capabilities='C', Options={})
SimulationStep is a function that performs a simulation step Obs is the observer that stores statisti...
def closeEvent(self, event)
close satelite windows and stops the simulation
def keyPressEvent(self, e)
processes actions such as Run, Step, Help...
def ReturnFromThread(self)
Processes graphic orders if in visible state returns -1 if Observer says the simulation is over.
def Raise(self)
if self.closeEvent(None): QtCore.QCoreApplication.instance().quit()
def SWDestroyed(self, SW)
A satellite window has been destroyed - removes it from the list.
def EventInterpreter(self, Event)
Sends event to observer (useful for mouse events)
def Process_graph_orders(self)
Just let Observer know that display has taken place.
Various functions.
Definition: Tools.py:1
def error(ErrMsg, Explanation='')
Definition: Tools.py:182