Evolife
Evolife has been developed to study Genetic algorithms, Natural evolution and behavioural ecology.
Evolife_Graphic.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2""" @brief Windows that display Genomes, Labyrinth and Social networks for Evolife.
3
4 Useful classes are:
5 - Genome_window: An image area that displays binary genomes
6 - Network_window: A drawing area that displays social links
7 - Field_window: A drawing area that displays agent movements
8"""
9
10#============================================================================#
11# EVOLIFE http://evolife.telecom-paris.fr Jean-Louis Dessalles #
12# Telecom Paris 2022-04-11 www.dessalles.fr #
13# -------------------------------------------------------------------------- #
14# License: Creative Commons BY-NC-SA #
15#============================================================================#
16# Documentation: https://evolife.telecom-paris.fr/Classes #
17#============================================================================#
18
19
20
23
24
25
26import sys
27if __name__ == '__main__': sys.path.append('../..') # for tests
28
29try:
30 from PyQt5 import QtGui, QtCore, QtWidgets
31 # grabWidget = QtGui.QScreen.grabWindow
32 grabWidget = QtWidgets.QWidget.grab
33except ImportError: # compatibility with PyQt4
34 from PyQt4 import QtGui, QtCore
35 from PyQt4 import QtGui as QtWidgets
36 grabWidget = QtGui.QPixmap.grabWidget
37
38import os.path
39import sys
40from time import sleep
41from math import ceil
42sys.path.append('..')
43
44
45from Evolife.Graphics.Plot_Area import Image_Area, Draw_Area, Ground
46from Evolife.Tools.Tools import Nb2A0, warning
47
48
49
53 """ returned information after click
54 """
55 def __init__(self, EmittingClass, EventType, Info):
56 self.EmittingClass = EmittingClass
57 self.EventType = EventType
58 self.Info = Info
59
60 def __str__(self):
61 return '%s/%s: %s' % (self.EmittingClass, self.EventType, str(self.Info))
62
63
64
67
68class AreaView(QtWidgets.QGraphicsView):
69 """ Standard canvas plus resizing capabilities
70 """
71 def __init__(self, AreaType=Image_Area, parent=None, image=None, width=400, height=300, zoom=1):
72 """ Defining View: a window (QGraphicsView) that contains a plot area (QGraphicsScene)
73 """
74 QtWidgets.QGraphicsView.__init__(self, parent) # calling the parent's constructor
75 if AreaType:
76 # View is a kind of camera on Area
77 self.Area = AreaType(image, width=width, height=height, EventInterpreter=self.EventInterpreter, zoom=zoom)
78 self.setScene(self.Area)
79 self.resize(self.Area.W, self.Area.H)
80 else: error("AreaView", "No area to display")
81 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) # necessary to avoir infinite loop with resizing events
82 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
83 # self.setMinimumSize(180, 180) # apparently necessary for PyQt5
84
85 def paintEvent(self, e):
86 """ calls Qt's paintEvent
87 """
88 QtWidgets.QGraphicsView.paintEvent(self,e)
89
90 def resizeEvent(self,e):
91 """ calls Qt's resizeEvent
92 """
93 if self.Area is not None:
94 self.Area.resize(e.size().width(), e.size().height())
95 QtWidgets.QGraphicsView.resizeEvent(self,e)
96
97 def updateScene(self, L):
98 """ unused
99 """
100 pass
101
102 def photo(self, Name, FrameNumber=-1, outputDir='.', extension='png'):
103 """ takes a snapshot and saves it to a new file
104 """
105 if FrameNumber >= 0:
106 self.FrameNumber = FrameNumber
107 else:
108 self.FrameNumber += 1
109 sleep(0.1)
110 picture = grabWidget(self)
111 ImFileName = Name + Nb2A0(self.FrameNumber)
112 picture.save(os.path.join(outputDir, ImFileName + '.' + extension))
113 return ImFileName
114
115 def EventInterpreter(self, Event):
116 """ Does nothing here. To be overloaded
117 """
118 # if Event[0] == 'MouseClick': print(Event)
119 pass
120
121
122
125
127 """ An Active_frame reacts to basic keyboard control
128 """
129 def __init__(self, AreaType=None, parent=None, control=None, image=None, width=400, height=300, zoom=1):
130 """ Creates a window (AreaView) with the appropriate AreaType (Draw_Area, Ground...)
131 """
132 if AreaType is not None:
133 # calling the parents' constructor
134 AreaView.__init__(self, AreaType=AreaType, parent=parent, image=image,
135 width=width, height=height, zoom=zoom)
136 else:
137 # print(self.__class__.__mro__)
138 QtWidgets.QWidget.__init__(self, parent=parent)
139 # super(QtWidgets.QWidget, self).__init__()
140 self.AreaArea = None
141 self.Parent = parent
142 self.Control = control # memorizing who is in charge (buttons)
143 if self.Control is None:
144 self.control = self.Parent
145
146 def keyPressEvent(self, e):
147 """ Definition of keyboard shortcuts
148 """
149 if e.key() in [QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape]:
150 self.close()
151 elif e.key() == QtCore.Qt.Key_M:
152 self.Control.Raise()
153 #self.Control.raise_()
154 #self.Control.activateWindow()
155 else:
156 self.Control.keyPressEvent(e)
157
158 def Raise(self):
159 """ puts the window on top of display
160 """
161 self.raise_()
162 self.activateWindow()
163
164 def EventInterpreter(self, Event):
165 if self.Control:
166 self.Control.EventInterpreter(ViewEvent(type(self).__name__, Event[0], Event[1]))
167
168
171
173 """ Satellite windows are floating windows with zooming abilities
174 """
175 def __init__(self, AreaType=None, control=None, Wtitle='', image=None, width=400, height=300, zoom=1):
176 """ calls the parents' constructor with the appropriate area (QGraphicsScene) type
177 """
178 Active_Frame.__init__(self, AreaType=AreaType, control=control, image=image,
179 width=width, height=height, zoom=zoom)
180 self.Title = Wtitle
181 self.setWindowTitle(Wtitle)
182 self.show()
183 self.minSize = 8
184 self.setMinimumSize(int(180*zoom), int(180*zoom)) # apparently necessary for PyQt5
185 self.zoomingFactor = zoom
186 self.Zoom(ZoomFactor=zoom)
187
188 def dimension(self, *geometry):
189 """ sets dimensions
190 """
191 if len(geometry) == 1:
192 width = geometry[0] * self.zoomingFactor
193 height = float(width)/self.AreaArea.W * self.AreaArea.H
194 posx, posy = 0,0
195 else:
196 geometry = list(geometry) # [x,y,w,h] with optional x,y
197 geometry.reverse()
198 geometry += [0, 0][:4-len(geometry)]
199 # height, width, posy, posx = list(map(lambda x: x * self.zoomingFactor, geometry))
200 height, width, posy, posx = geometry
201 if posx and posy: self.setGeometry(posx, posy, width, height)
202 else: self.resize(width, height) # used at initialization
203 # return self.Area.dimension()
204
205 def keyPressEvent(self, e):
206 """ adds resizing keys (+ and -) to Active_Frame's shortcuts
207 """
208 Active_Frame.keyPressEvent(self,e)
209 # Additional key actions
210 if e.key() in [QtCore.Qt.Key_Z, QtCore.Qt.Key_Minus]:
211 self.DeZoom()
212 if e.key() in [QtCore.Qt.Key_Plus]:
213 self.Zoom()
214
215 def image_display(self, Image, windowResize=True):
216 """ display an image, with possible resizing of the window
217 """
218 if Image is None or not os.path.exists(str(Image)): return
219 self.AreaArea.Board = QtGui.QPixmap(Image) # loads the image
220 if windowResize:
221 # newWidth = self.Area.W
222 newWidth = min(800, self.AreaArea.Board.width())
223 newHeight = min(600, self.AreaArea.Board.height())
224 try: zoomFactor = min(float(newWidth) / self.AreaArea.Board.width(), float(newHeight) / self.AreaArea.Board.height())
225 except ZeroDivisionError: zoomFactor = 1
226 self.resize(int(self.AreaArea.Board.width()*zoomFactor), int(self.AreaArea.Board.height()*zoomFactor))
227 self.AreaArea.redraw()
228 else: self.AreaArea.redraw()
229 self.setWindowTitle(self.Title + ' - ' + Image)
230
231 def Zoom(self, ZoomFactor=1.1):
232 """ increase the window's size
233 """
234 self.resize(int(self.width()*ZoomFactor),int(self.height()*ZoomFactor))
235
236 def DeZoom(self, DeZoomFactor=0.91):
237 """ decrease the window's size
238 """
239 self.resize(int(self.width()*DeZoomFactor),int(self.height()*DeZoomFactor))
240
241 def closeEvent(self, event):
242 """ destroys the window
243 """
244 if self.Control is not None:
245 try:
246 self.Control.SWDestroyed(self) # should be done with a signal
247 except Exception as Msg: print(Msg)
248 event.accept()
249
250
253
255 """ Image_window: Merely contains an image area
256 """
257 def __init__(self, control=None, Wtitle='', outputDir='.'):
258 """ calls Satellite_window's constructor
259 """
260 self.OutputDir = outputDir
261 self.W = 300
262 self.H = 200
263 self.defaultSize = True # will become false when we know genome size
264 Satellite_window.__init__(self, Draw_Area, control=control, Wtitle='Images', width=self.W, height=self.H)
265 self.AreaArea.set_margins(1, 1, 1, 1)
266
267
268
271
273 """ Genome_window: An image area that displays binary genomes
274 """
275 def __init__(self, control=None, image=None, genome=None, gene_pattern=None, outputDir='.', zoom=1):
276 """ calls Satellite_window's constructor
277 and performs first display
278 """
279 self.gene_pattern = gene_pattern
280 self.OutputDir = outputDir
281 self.H = 100
282 self.W = 100
283 self.defaultSize = True # will become false when we know genome size
284 if genome is not None:
285 self.H = len(genome)
286 self.W = len(genome[0])
287 self.defaultSize = False
288 Satellite_window.__init__(self, Draw_Area, control=control, Wtitle='Genomes', image=image, width=self.W, height=self.H, zoom=zoom)
289 self.minSizeminSize = 100
290 self.AreaArea.set_margins(1, 1, 1, 1)
291 if genome is not None:
292 self.genome_display(genome=genome, gene_pattern=self.gene_pattern)
293 self.AreaArea.grid = self.axes
294
295 def axes(self):
296 """ draws separation between genes
297 """
298 if self.gene_pattern is None: return
299 gridPen = QtGui.QPen()
300 gridPen.setColor(QtGui.QColor('#FF0000')) # red lines to indicate gene limit
301 gridPen.setWidth(1)
302 pattern = list(self.gene_pattern)
303 G = 1
304 HPos = 0
305 while G in pattern:
306 # vertical lines
307 HPos += (pattern.index(G) * self.AreaArea.W)/self.W
308 self.AreaArea.addLine( self.AreaArea.LeftMargin + HPos, self.AreaArea.TopMargin,
309 self.AreaArea.LeftMargin + HPos, self.AreaArea.H - self.AreaArea.BottomMargin, gridPen)
310 del pattern[:pattern.index(G)]
311 G = 1-G
312
313 def genome_display(self, genome=None, gene_pattern=(), Photo=0, CurrentFrame=-1, Prefix=''):
314 """ genome gives, for each individual, the sequence of binary nucleotides
315 gene_pattern is a binary flag to signal gene alternation
316 """
317
318 PhotoName = ''
319 if Photo:
320 if Prefix == '': Prefix = '___Genome_'
321 PhotoName = self.photo(Prefix, CurrentFrame, outputDir=self.OutputDir)
322
323 if genome is None or len(genome) == 0: return ''
324 if gene_pattern is not None: self.gene_pattern = gene_pattern
325 self.H = len(genome)
326 self.W = len(genome[0])
327 if self.defaultSize:
328 self.resize(max(self.W, self.minSizeminSize), max(self.H, self.minSizeminSize))
329 self.defaultSize = False
330
331 #GenomeImg = QtGui.QImage(W,H,QtGui.QImage.Format_RGB32) # why not this (?) format ?
332 GenomeImg = QtGui.QImage(self.W, self.H, QtGui.QImage.Format_Mono)
333
334 for line in range(self.H):
335 for pixel in range(self.W):
336 if genome[line][pixel]:
337 GenomeImg.setPixel(pixel,line, 1)
338 else:
339 GenomeImg.setPixel(pixel,line, 0)
340 # We should add the bitmap with window background. Don't know how to do this
341 self.AreaArea.Board = QtGui.QBitmap.fromImage(GenomeImg.scaled(self.AreaArea.W,self.AreaArea.H))
342 self.AreaArea.redraw()
343 return PhotoName
344
345
346
349
351 """ Network_window: A drawing area that displays social links
352 The population is displayed twice, on two horizontal axes.
353 Social links are displayed as ascending vectors from one individual
354 on the bottom line to another on the upper line.
355 """
356 def __init__(self, control, image=None, outputDir='.', width=540, height=200, zoom=1):
357 """ calls Satellite_window's constructor
358 """
359 Satellite_window.__init__(self, Draw_Area, control=control, Wtitle='Social network',
360 width=width, height=height, image=image, zoom=zoom)
361 self.OutputDir = outputDir
362 #self.Area.grid = self.axes
363 # self.Area.Board.fill(QtGui.QColor(QtCore.Qt.white))
364 self.AreaArea.set_margins(20,20,20,20)
365 self.axes()
366 self.friends = {}
367
368
369 def axes(self):
370 """ Draws two horizontal axes; each axis represents the population;
371 social links are shown as vectors going from the lower line
372 to the upper one
373 """
374 self.AreaArea.move(6, (0, self.AreaArea.scaleY))
375 self.AreaArea.plot(6, (self.AreaArea.scaleX, self.AreaArea.scaleY))
376 self.AreaArea.move(6, (0, 0))
377 self.AreaArea.plot(6, (self.AreaArea.scaleX, 0))
378
379 def Network_display(self, Layout, network=None, Photo=0, CurrentFrame=-1, Prefix=''):
380 """ Social links are displayed as ascending vectors from one individual
381 on the bottom line to another on the upper line.
382 """
383 PhotoName = ''
384 if Photo:
385 PhotoName = self.Dump_network(self.friends, CurrentFrame, Prefix=Prefix)
386
387 # print(network)
388 if not network: return ''
389 positions = dict([L for L in Layout if len(L) == 2 and type(L[1]) == tuple]) # positions of individuals
390 if positions == {}: return None
391 self.friends = dict(network)
392 self.AreaArea.scaleX = max(self.AreaArea.scaleX, max([positions[individual][0] for individual in positions]))
393 self.AreaArea.erase()
394 self.axes()
395 for individual in self.friends:
396 if len(self.friends[individual]):
397 bestFriend = self.friends[individual][0]
398 self.AreaArea.move(6, (positions[individual][0],0))
399 try:
400 self.AreaArea.plot(6, (positions[bestFriend][0],self.AreaArea.scaleY), 2)
401 except KeyError: warning('friend has vanished', bestFriend)
402
407 return PhotoName
408
409 def Dump_network(self, friends, CurrentFrame=-1, Prefix=''):
410 """ stores social links into a matrix written into a file
411 """
412 if Prefix == '': Prefix = '___Network_'
413 PhotoName = self.photo(Prefix, CurrentFrame, outputDir=self.OutputDir)
414 MatrixFileName = os.path.join(self.OutputDir, Prefix + Nb2A0(self.FrameNumber) + '.txt')
415 MatrixFile = open(MatrixFileName,'w')
416 for Individual in friends:
417 MatrixFile.write(str(Individual))
418 for F in friends[Individual]:
419 MatrixFile.write('\t%s' % F)
420 MatrixFile.write('\n')
421 MatrixFile.close()
422 return PhotoName
423
424
425
428
430 """ Field: A 2D widget that displays agent movements
431 """
432
433 def __init__(self, control=None, Wtitle='', image=None, outputDir='.', width=400, height=300, zoom=1):
434 if image:
435 Satellite_window.__init__(self, Ground, control=control, Wtitle=Wtitle, image=image, zoom=zoom)
436 self.image_display(image, windowResize=True) # to resize
437 else:
438 Satellite_window.__init__(self, Ground, control=control, Wtitle=Wtitle, image=None, width=width, height=height, zoom=zoom)
439 self.FreeScale = not self.AreaArea.fitSize # no physical rescaling has occurred - useful to shrink logical scale to acual data
440 if self.FreeScale:
441 self.AreaArea.scaleX = 1.0 # virtual coordinates by default
442 self.AreaArea.scaleY = 1.0 # virtual coordinates by default
443 # self.Area.redraw()
444 self.OutputDir = outputDir
445 self.AreaArea.grid()
446
447 def Field_display(self, Layout=None, Photo=0, CurrentFrame=-1, Ongoing=False, Prefix=''):
448 """ displays agents at indicated positions
449 If Ongoing is false, agents that are not given positions are removed
450
451 Layout may come with two syntaxes:
452 - ((Agent1Id, Coordinates1), (Agent2Id, Coordinates2), ...)
453 - (Coordinates1, Coordinates2, ...)
454 The first format allows to move and erase agents
455 The second format is merely used to draw permanent blobs and lines
456 Coordinates have the following form:
457 (x, y, colour, size, ToX, ToY, segmentColour, segmentThickness, 'shape=<form>')
458 (shorter tuples are automatically continued with default values - 'shape=...' can be inserted anywhere)
459 The effect is that an object of size 'size' is drawn at location (x,y) (your coordinates, not pixels)
460 and a segment starting from that blob is drawn to (ToX, ToY) (if these values are given)
461 If you change the coordinates of an agent in the next call, it will be moved.
462 'size' is in pixels and is not resized in case of zoom. However, if negative, it is interpreted in your coordinates and it will be resized/
463 'size' may be a fractional number (float). It is then understood as a fraction of the window size.
464 The value assigned to 'shape' in the string 'shape=...' can be 'ellipse' (=default) or 'rectangle' or
465 any image. If it is an image, 'colour' is interpreted as an angle. The image is scaled to fit 'size' (but aspect ratio is preserved)
466
467 These two forms of vectors can be used to draw in two windows: 'Trajectories' and 'Field'.
468 Use the order: record(vector, Window=<'Field'|'Trajectories'>)
469 or: record([vectors], Window=<'Field'|'Trajectories'>)
470 'Field' is default. The latter order is used to send a list of vectors.
471
472 The 'Field' window comes in two modes, 'F' and 'R' (see option F and R at Evolife's start)
473 - In the 'F' mode, all agents should be given positions at each call.
474 Missing agents are destroyed from display.
475 - In the 'R' ('Region') mode, you may indicates positions only for relevant agents
476 To destroy an agent from display, give a negative value to its colour.
477 """
478
479 PhotoName = ''
480 if Photo:
481 if Prefix == '': Prefix = '___Field_'
482 PhotoName = self.photo(Prefix, CurrentFrame, outputDir=self.OutputDir)
483
484 if not Layout: return ''
485
486
487 # separating agents from mere coordinates
488 AgentLayout = dict([L for L in Layout if len(L) == 2 and type(L[1]) == tuple]) # positions of individuals
489 DrawingLayout = [L for L in Layout if len(L) != 2 or type(L[1]) != tuple]
490
491 # print(' '.join(AgentLayout.keys()))
492 if DrawingLayout:
493 if DrawingLayout == ['erase']:
494 self.erase()
495 else:
496 # adapting scale
497 if self.FreeScale: self.adaptscale(DrawingLayout)
498 # agent names are not given, Layout designates mere drawing instructions and not agents
499 for Pos in DrawingLayout:
500 # if Pos == 'erase': self.erase()
501 self.AreaArea.draw_tailed_blob(Pos)
502
503 if AgentLayout:
504 # adapting scale at first call
505 if self.FreeScale: self.adaptscale(AgentLayout.values())
506 # getting the list of agents already present
507 # existing agents that are not redrawn are removed
508 if not Ongoing: self.AreaArea.remove_absent(AgentLayout.keys())
509 for Individual in AgentLayout:
510 self.AreaArea.move_agent(Individual, AgentLayout[Individual])
511 # creates agent if not already existing
512
513
514 # self.FreeScale = False
515
516 return PhotoName
517
518 def erase(self):
519 """ calls Area erase
520 """
521 self.AreaArea.erase()
522
523 def adaptscale(self, Layout):
524 """ rescales widget if items land outside (only for max values, not for negative ones)
525 """
526 newScaleX = max(self.AreaArea.scaleX, max([pos[0] for pos in Layout]))
527 newScaleY = max(self.AreaArea.scaleY, max([pos[1] for pos in Layout]))
528 if (newScaleX, newScaleY) != (self.AreaArea.scaleX, self.AreaArea.scaleY):
529 self.AreaArea.scaleX, self.AreaArea.scaleY = ceil(newScaleX), ceil(newScaleY)
530 # print(self.Title, self.Area.scaleX, self.Area.scaleY)
531 self.AreaArea.redraw()
532
533 def Field_scroll(self):
534 self.AreaArea.scroll()
535
537 """ Synonymous for Field_window
538 """
539 pass
540
541
542class Help_window(QtWidgets.QTextBrowser):
543 """ Displays a text file supposed to provide help
544 """
545 def __init__(self, Control=None, Wtitle='Help'):
546 """ calls Qt's QTextBrowser
547 """
548 QtWidgets.QTextBrowser.__init__(self)
549 self.setWindowTitle(Wtitle)
550 self.Control = Control
551
552 def keyPressEvent(self, e):
553 """ Definition of keyboard shortcuts
554 """
555 if e.key() in [QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape]:
556 self.close()
557 elif e.key() == QtCore.Qt.Key_M:
558 self.Control.Raise()
559 else:
560 self.Control.keyPressEvent(e)
561
562 def display(self, HelpFilename):
563 """ show help window
564 """
565 self.setPlainText(open(HelpFilename).read())
566 self.setOverwriteMode(False)
567 self.show()
568
569 def Raise(self):
570 """ puts help widget in front of display
571 """
572 self.raise_()
573 self.activateWindow()
574
575 def closeEvent(self, event):
576 """ destroys help widget
577 """
578 if self.Control is not None:
579 try:
580 self.Control.SWDestroyed(self) # should be done with a signal
581 except Error as Msg:
582 print(Msg)
583 event.accept()
584
586 """ displays legend for curves
587 """
588 def __init__(self, Control=None, Wtitle='Legend'):
589 """ legend window is copied from help window
590 """
591 Help_window.__init__(self, Control=Control, Wtitle=Wtitle)
592
593 def display(self, Legend, Comments=''):
594 """ Legend comes as a list of couples (ColourName, Meaning)
595 """
596 # self.setPlainText(Text + '\n\npress [Esc]')
597 self.setOverwriteMode(False)
598
599 # self.insertPlainText('\nCurves:')
600 self.insertHtml('<P><u>Curves</u>:<br>')
601 try:
602 for (CID, Ccolour, Ccolourname, CName, CLegend) in Legend:
603 # self.insertPlainText('\n')
604 if CID == 2: # white colour, printed in black
605 self.insertHtml('<br><b><font color="black">%s:</font></b>' % (Ccolour))
606 else:
607 self.insertHtml('<br><b><font color="%s">%s:</font></b>' % (Ccolour, Ccolourname))
608 self.insertPlainText('\t')
609 self.insertHtml('%s' % CLegend)
610 except IndexError:
611 error("Curves: unknown Curve ID")
612
613 if Comments:
614 self.insertPlainText('\n')
615 self.insertHtml('%s' % Comments)
616 self.insertPlainText('\n=============\n( [Esc] to close )')
617
618 # resizing window around text (from http://stackoverflow.com/questions/9506586/qtextedit-resize-to-fit )
619 text = self.document().toPlainText() # or another font if you change it
620 font = self.document().defaultFont() # or another font if you change it
621 fontMetrics = QtGui.QFontMetrics(font) # a QFontMetrics based on our font
622 textSize = fontMetrics.size(0, text)
623 textWidth = textSize.width() + 30 # constant may need to be tweaked
624 textHeight = textSize.height() + 30 # constant may need to be tweaked
625 self.setMinimumSize(textWidth, textHeight) # good if you want to insert this into a layout
626 # self.resize(textWidth, textHeight) # good if you want this to be standalone
627
628 self.moveCursor(QtGui.QTextCursor.Start)
629 self.ensureCursorVisible() ;
630
631
632
633 # self.setTextColor(QColor(EvolifeColourID(Position.colour)[1]))
634 self.show()
635
636
637
640
641if __name__ == "__main__":
642
643 print(__doc__)
644
645
646__author__ = 'Dessalles'
An Active_frame reacts to basic keyboard control.
def EventInterpreter(self, Event)
Does nothing here.
def Raise(self)
puts the window on top of display
def __init__(self, AreaType=None, parent=None, control=None, image=None, width=400, height=300, zoom=1)
Creates a window (AreaView) with the appropriate AreaType (Draw_Area, Ground...)
def keyPressEvent(self, e)
Definition of keyboard shortcuts.
Standard canvas plus resizing capabilities.
def photo(self, Name, FrameNumber=-1, outputDir='.', extension='png')
takes a snapshot and saves it to a new file
def resizeEvent(self, e)
calls Qt's resizeEvent
def EventInterpreter(self, Event)
Does nothing here.
def paintEvent(self, e)
calls Qt's paintEvent
def __init__(self, AreaType=Image_Area, parent=None, image=None, width=400, height=300, zoom=1)
Defining View: a window (QGraphicsView) that contains a plot area (QGraphicsScene)
A graphic area that displays moving agents #.
def __init__(self, control=None, Wtitle='', image=None, outputDir='.', width=400, height=300, zoom=1)
calls the parents' constructor with the appropriate area (QGraphicsScene) type
def adaptscale(self, Layout)
rescales widget if items land outside (only for max values, not for negative ones)
def Field_display(self, Layout=None, Photo=0, CurrentFrame=-1, Ongoing=False, Prefix='')
displays agents at indicated positions If Ongoing is false, agents that are not given positions are r...
Genome_window: An image area that displays binary genomes.
def genome_display(self, genome=None, gene_pattern=(), Photo=0, CurrentFrame=-1, Prefix='')
genome gives, for each individual, the sequence of binary nucleotides gene_pattern is a binary flag t...
def axes(self)
draws separation between genes
def __init__(self, control=None, image=None, genome=None, gene_pattern=None, outputDir='.', zoom=1)
calls Satellite_window's constructor and performs first display
Displays a text file supposed to provide help.
def keyPressEvent(self, e)
Definition of keyboard shortcuts.
def __init__(self, Control=None, Wtitle='Help')
calls Qt's QTextBrowser
def display(self, HelpFilename)
show help window
def closeEvent(self, event)
destroys help widget
def Raise(self)
puts help widget in front of display
Image_window: Merely contains an image area.
def __init__(self, control=None, Wtitle='', outputDir='.')
calls Satellite_window's constructor
def __init__(self, Control=None, Wtitle='Legend')
legend window is copied from help window
def display(self, Legend, Comments='')
Legend comes as a list of couples (ColourName, Meaning)
Network_window: A drawing area that displays social links The population is displayed twice,...
def Network_display(self, Layout, network=None, Photo=0, CurrentFrame=-1, Prefix='')
Social links are displayed as ascending vectors from one individual on the bottom line to another on ...
def axes(self)
Draws two horizontal axes; each axis represents the population; social links are shown as vectors goi...
Satellite windows are floating windows with zooming abilities.
def keyPressEvent(self, e)
adds resizing keys (+ and -) to Active_Frame's shortcuts
def Zoom(self, ZoomFactor=1.1)
increase the window's size
def DeZoom(self, DeZoomFactor=0.91)
decrease the window's size
def dimension(self, *geometry)
sets dimensions
def __init__(self, AreaType=None, control=None, Wtitle='', image=None, width=400, height=300, zoom=1)
calls the parents' constructor with the appropriate area (QGraphicsScene) type
def image_display(self, Image, windowResize=True)
display an image, with possible resizing of the window
def closeEvent(self, event)
destroys the window
def __init__(self, EmittingClass, EventType, Info)
def Dump_network(self, friends, CurrentFrame=-1, Prefix='')
if len(self.friends[friend]) and individual == self.friends[friend][0]: self.plot(6,...
def SWDestroyed(self, SW)
A satellite window has been destroyed - removes it from the list.
Windows that display Curves or images.
Definition: Plot_Area.py:1
Various functions.
Definition: Tools.py:1
def warning(WMsg, Explanation='')
Definition: Tools.py:197
def error(ErrMsg, Explanation='')
Definition: Tools.py:182
def Nb2A0(Nb)
converts a number into a padded string
Definition: Tools.py:135