2""" @brief Windows that display Genomes, Labyrinth and Social networks
for Evolife.
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
10#============================================================================#
11# EVOLIFE http://evolife.telecom-paris.fr Jean-Louis Dessalles
27if __name__ ==
'__main__': sys.path.append(
'../..')
30 from PyQt5
import QtGui, QtCore, QtWidgets
32 grabWidget = QtWidgets.QWidget.grab
34 from PyQt4
import QtGui, QtCore
35 from PyQt4
import QtGui
as QtWidgets
36 grabWidget = QtGui.QPixmap.grabWidget
53 """ returned information after click
55 def __init__(self, EmittingClass, EventType, Info):
69 """ Standard canvas plus resizing capabilities
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)
74 QtWidgets.QGraphicsView.__init__(self, parent)
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)
82 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
86 """ calls Qt's paintEvent
88 QtWidgets.QGraphicsView.paintEvent(self,e)
91 """ calls Qt's resizeEvent
93 if self.
Area is not None:
94 self.
Area.resize(e.size().width(), e.size().height())
95 QtWidgets.QGraphicsView.resizeEvent(self,e)
102 def photo(self, Name, FrameNumber=-1, outputDir='.', extension='png'):
103 """ takes a snapshot and saves it to a new file
112 picture.save(os.path.join(outputDir, ImFileName +
'.' + extension))
116 """ Does nothing here. To be overloaded
127 """ An Active_frame reacts to basic keyboard control
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...)
132 if AreaType
is not None:
134 AreaView.__init__(self, AreaType=AreaType, parent=parent, image=image,
135 width=width, height=height, zoom=zoom)
138 QtWidgets.QWidget.__init__(self, parent=parent)
147 """ Definition of keyboard shortcuts
149 if e.key()
in [QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape]:
151 elif e.key() == QtCore.Qt.Key_M:
159 """ puts the window on top of display
162 self.activateWindow()
173 """ Satellite windows are floating windows with zooming abilities
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
178 Active_Frame.__init__(self, AreaType=AreaType, control=control, image=image,
179 width=width, height=height, zoom=zoom)
181 self.setWindowTitle(Wtitle)
184 self.setMinimumSize(int(180*zoom), int(180*zoom))
186 self.
Zoom(ZoomFactor=zoom)
191 if len(geometry) == 1:
196 geometry = list(geometry)
198 geometry += [0, 0][:4-len(geometry)]
200 height, width, posy, posx = geometry
201 if posx
and posy: self.setGeometry(posx, posy, width, height)
202 else: self.resize(width, height)
206 """ adds resizing keys (+ and -) to Active_Frame's shortcuts
208 Active_Frame.keyPressEvent(self,e)
210 if e.key()
in [QtCore.Qt.Key_Z, QtCore.Qt.Key_Minus]:
212 if e.key()
in [QtCore.Qt.Key_Plus]:
216 """ display an image, with possible resizing of the window
218 if Image
is None or not os.path.exists(str(Image)):
return
219 self.
AreaArea.Board = QtGui.QPixmap(Image)
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))
229 self.setWindowTitle(self.
Title +
' - ' + Image)
231 def Zoom(self, ZoomFactor=1.1):
232 """ increase the window's size
234 self.resize(int(self.width()*ZoomFactor),int(self.height()*ZoomFactor))
237 """ decrease the window's size
239 self.resize(int(self.width()*DeZoomFactor),int(self.height()*DeZoomFactor))
242 """ destroys the window
247 except Exception
as Msg: print(Msg)
255 """ Image_window: Merely contains an image area
257 def __init__(self, control=None, Wtitle='', outputDir='.'):
258 """ calls Satellite_window's constructor
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)
273 """ Genome_window: An image area that displays binary genomes
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
284 if genome
is not None:
286 self.
W = len(genome[0])
288 Satellite_window.__init__(self, Draw_Area, control=control, Wtitle=
'Genomes', image=image, width=self.
W, height=self.
H, zoom=zoom)
290 self.
AreaArea.set_margins(1, 1, 1, 1)
291 if genome
is not None:
296 """ draws separation between genes
299 gridPen = QtGui.QPen()
300 gridPen.setColor(QtGui.QColor(
'#FF0000'))
307 HPos += (pattern.index(G) * self.
AreaArea.W)/self.
W
310 del pattern[:pattern.index(G)]
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
320 if Prefix ==
'': Prefix =
'___Genome_'
321 PhotoName = self.
photo(Prefix, CurrentFrame, outputDir=self.
OutputDir)
323 if genome
is None or len(genome) == 0:
return ''
324 if gene_pattern
is not None: self.
gene_pattern = gene_pattern
326 self.
W = len(genome[0])
332 GenomeImg = QtGui.QImage(self.
W, self.
H, QtGui.QImage.Format_Mono)
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)
339 GenomeImg.setPixel(pixel,line, 0)
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.
356 def __init__(self, control, image=None, outputDir='.', width=540, height=200, zoom=1):
357 """ calls Satellite_window's constructor
359 Satellite_window.__init__(self, Draw_Area, control=control, Wtitle='Social network',
360 width=width, height=height, image=image, zoom=zoom)
364 self.
AreaArea.set_margins(20,20,20,20)
370 """ Draws two horizontal axes; each axis represents the population;
371 social links are shown as vectors going
from the lower line
380 """ Social links are displayed as ascending vectors from one individual
381 on the bottom line to another on the upper line.
385 PhotoName = self.Dump_network(self.
friends, CurrentFrame, Prefix=Prefix)
388 if not network:
return ''
389 positions = dict([L
for L
in Layout
if len(L) == 2
and type(L[1]) == tuple])
390 if positions == {}:
return None
392 self.
AreaArea.scaleX = max(self.
AreaArea.scaleX, max([positions[individual][0]
for individual
in positions]))
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))
401 except KeyError:
warning(
'friend has vanished', bestFriend)
410 """ stores social links into a matrix written into a file
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')
430 """ Field: A 2D widget that displays agent movements
433 def __init__(self, control=None, Wtitle='', image=None, outputDir='.', width=400, height=300, zoom=1):
435 Satellite_window.__init__(self, Ground, control=control, Wtitle=Wtitle, image=image, zoom=zoom)
438 Satellite_window.__init__(self, Ground, control=control, Wtitle=Wtitle, image=
None, width=width, height=height, zoom=zoom)
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
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)
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.
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.
481 if Prefix ==
'': Prefix =
'___Field_'
482 PhotoName = self.
photo(Prefix, CurrentFrame, outputDir=self.
OutputDir)
484 if not Layout:
return ''
488 AgentLayout = dict([L
for L
in Layout
if len(L) == 2
and type(L[1]) == tuple])
489 DrawingLayout = [L
for L
in Layout
if len(L) != 2
or type(L[1]) != tuple]
493 if DrawingLayout == [
'erase']:
499 for Pos
in DrawingLayout:
508 if not Ongoing: self.
AreaArea.remove_absent(AgentLayout.keys())
509 for Individual
in AgentLayout:
510 self.
AreaArea.move_agent(Individual, AgentLayout[Individual])
524 """ rescales widget if items land outside (only for max values, not for negative ones)
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]))
529 self.
AreaArea.scaleX, self.
AreaArea.scaleY = ceil(newScaleX), ceil(newScaleY)
537 """ Synonymous for Field_window
542class Help_window(QtWidgets.QTextBrowser):
543 """ Displays a text file supposed to provide help
546 """ calls Qt's QTextBrowser
548 QtWidgets.QTextBrowser.__init__(self)
549 self.setWindowTitle(Wtitle)
553 """ Definition of keyboard shortcuts
555 if e.key()
in [QtCore.Qt.Key_Q, QtCore.Qt.Key_Escape]:
557 elif e.key() == QtCore.Qt.Key_M:
565 self.setPlainText(open(HelpFilename).read())
566 self.setOverwriteMode(False)
570 """ puts help widget in front of display
573 self.activateWindow()
576 """ destroys help widget
586 """ displays legend for curves
589 """ legend window is copied from help window
591 Help_window.__init__(self, Control=Control, Wtitle=Wtitle)
594 """ Legend comes as a list of couples (ColourName, Meaning)
597 self.setOverwriteMode(
False)
600 self.insertHtml(
'<P><u>Curves</u>:<br>')
602 for (CID, Ccolour, Ccolourname, CName, CLegend)
in Legend:
605 self.insertHtml(
'<br><b><font color="black">%s:</font></b>' % (Ccolour))
607 self.insertHtml(
'<br><b><font color="%s">%s:</font></b>' % (Ccolour, Ccolourname))
608 self.insertPlainText(
'\t')
609 self.insertHtml(
'%s' % CLegend)
611 error(
"Curves: unknown Curve ID")
614 self.insertPlainText(
'\n')
615 self.insertHtml(
'%s' % Comments)
616 self.insertPlainText(
'\n=============\n( [Esc] to close )')
619 text = self.document().toPlainText()
620 font = self.document().defaultFont()
621 fontMetrics = QtGui.QFontMetrics(font)
622 textSize = fontMetrics.size(0, text)
623 textWidth = textSize.width() + 30
624 textHeight = textSize.height() + 30
625 self.setMinimumSize(textWidth, textHeight)
628 self.moveCursor(QtGui.QTextCursor.Start)
629 self.ensureCursorVisible() ;
641if __name__ ==
"__main__":
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 updateScene(self, L)
unused
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 erase(self)
calls Area erase
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
displays legend for curves
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
Synonymous for Field_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.