2""" @brief Windows that display Curves or images.
4 This module can be used independently.
7 - Image_Area: An area that displays an image
9 - Draw_Area: Basic drawing Area
10 function: plot(<plot_number>, (<x>, <y>) )
11 - Plot_Area: Draw_Area + grid + automatic resizing
12 function: plot(<plot_number>, (<x>, <y>) )
13 - Ground: Defines a 2-D region where agents are located
and may move
14 functions: create_agent(<name>, <colour_number>, (<x>, <y>) )
15 move_agent(<name>, (<Newx>, <Newy>) )
19 self.W = QPlot_Area.AreaView(QPlot_Area.Draw_Area)
21 self.W.Area.plot(3,(10,10))
22 self.W.Area.plot(3,(20,40))
23 self.W.Area.plot(3,(10,22))
26#============================================================================#
27# EVOLIFE http://evolife.telecom-paris.fr Jean-Louis Dessalles
44if __name__ ==
'__main__': sys.path.append(
'../..')
53 from PyQt5
import QtGui, QtCore, QtWidgets
56 from PyQt4
import QtGui, QtCore
57 from PyQt4
import QtGui
as QtWidgets
59 if sys.version_info >= (3,0): QString =
lambda x: x
60 else: QString = QtCore.QString
69ZoomL =
lambda *L: map(
lambda x: int(x * L[0]), L[1:])
77 """ Defines a logical Area on which objects are drawn (hidden from screen)
80 def __init__(self, image=None, width=1000, height=1000, EventInterpreter=None, zoom=1):
81 """ Inherits from QGraphicsScene
83 QtWidgets.QGraphicsScene.__init__(self, 0, 0, width, height)
84 if image
is not None and os.path.exists(str(image)):
93 if image_ID
is not None:
94 self.
Board.fill(QtGui.QColor(image_ID[1]))
95 elif image
and image.startswith(
'#'):
96 self.
Board.fill(QtGui.QColor(image))
99 self.
Board.fill(QtGui.QColor(
"#FFFFFF"))
102 self.
W = int(self.width())
103 self.
H = int(self.height())
113 """ Stores new dimensions and redraws
121 """ returns dimensions
123 return (self.width(), self.height())
126 """ the whole scene is redrawn when the scale changes
128 for obj
in self.items():
132 self.setSceneRect(self.itemsBoundingRect())
137 """ Unused ------ just for test
139 item = QtGui.QGraphicsEllipseItem(20, 10, 40, 20, None, self)
140 qp = QtGui.QPainter()
143 pen.setColor(QtCore.Qt.red)
147 x = random.randint(1, self.
W-1)
148 y = random.randint(1, self.
H-1)
149 qp.drawLine(0,0,x, y)
160 """ Draw_Area: Basic drawing Area
163 def __init__(self, image=None, width=400, height=400, EventInterpreter=None, zoom=1):
164 """ Calls Image_Area constructor
167 Image_Area.__init__(self, image, width, height, EventInterpreter, zoom=zoom)
178 Curves.__init__(self)
182 """ surrounding margins (in Image_Area pixels)
190 """ Defining one pen and one QPainterPath per curve
196 self.
Pens[Curve.ID] = QtGui.QPen()
197 self.
Pens[Curve.ID].setColor(QtGui.QColor(Curve.colour))
198 self.
Pens[Curve.ID].setWidth(3)
203 """ Initial display - to be overloaded
208 """ converts physical coordinates into logical pixel(between 0 and scaleX)
215 """ converts logical pixel (between 0 and scaleX) into physical coordinates through rescaling
224 """ Conversion of logical coordinates into physical coordinates through scaling and margin translation
230 """ Conversion of Point into Qt point
234 return QtCore.QPointF(x, y)
236 def draw(self, oldpoint, newpoint, Width=3, ColorID=0, Tag=''):
237 """ adds a segment on a given curve
243 self.
Pens[ColorID].setWidth(int(Width))
246 def drawTo(self, newpoint, Width=3, ColorID=0, Drawing=True, Tag=''):
247 """ draws an additional segment on a given curve (from the end of the previous one)
256 self.
draw(self.
Curves[ColorID].last(), newpoint, Width, ColorID, Tag)
260 """ erase curves, items and restore grid
264 for obj
in self.items():
271 """ the whole picture is redrawn when the scale changes
273 Image_Area.redraw(self)
277 for Segment
in Curve:
279 self.
draw(Segment[0], Segment[1], Width=Curve.thick, ColorID=Curve.ID)
282 """ performs a change of scale when necessary
285 def rescale(Scale, coord):
291 ordre = 10 ** (int(log(coord,10)))
292 Scale = (coord // ordre + 1) * ordre
297 if isinstance(Point, Stroke):
298 ws, hs = self.
pixel2xy((Point.size, Point.size))
300 (x,y) = (Point.x + ws, Point.y + hs)
306 def plot(self, Curve_designation, newpoint, Width=3):
307 """ draws an additional segment on a curve
314 self.
Curves[Curve_id].thick = Width
315 self.
drawTo(newpoint, Width, Curve_id)
318 error(
"Draw_Area: unknown Curve ID")
320 def move(self, Curve_designation, newpoint):
321 """ introduces a discontinuity in a curve
328 self.CurveAddPoint(Curve_id, newpoint, Draw=
False)
330 error(
"Draw_Area: unknown Curve ID")
332 def speck(self, Curve_id, newpoint, Size=3):
336 if self.reframe(newpoint): self.redraw()
337 self.move(Curve_id, newpoint)
338 self.plot(Curve_id, newpoint, Width=Size)
341 """ convert mouse position into window coordinates
343 mousePosition = MouseEvent.scenePos()
344 x = mousePosition.x() - self.LeftMargin
345 y = self.H - mousePosition.y() - self.BottomMargin
346 if x >= 0
and y >= 0
and self.EventInterpreter:
347 return self.pixel2xy((x,y))
351 """ retrieves mouse clicks - added by Gauthier Tallec
353 Loc = self.mouseLocate(MouseEvent)
355 self.EventInterpreter((
'MouseClick', Loc))
358 """ retrieves mouse clicks
360 Loc = self.mouseLocate(MouseEvent)
362 self.EventInterpreter((
'MouseDeClick', Loc))
365 """ retrieves mouse clicks
367 Loc = self.mouseLocate(MouseEvent)
369 self.EventInterpreter((
'MouseDoubleClick', Loc))
372 """ retrieves mouse movements when button is pressed
374 Loc = self.mouseLocate(MouseEvent)
376 self.EventInterpreter((
'MouseMove', Loc))
385 """ Definition of the drawing area, with grid, legend and curves
387 def __init__(self, image=None, width=556, height=390, EventInterpreter=None, zoom=1):
388 """ Calls Draw_Area constructor and sets margins
390 Draw_Area.__init__(self, image, width, height, EventInterpreter, zoom=zoom)
396 """ Drawing a grid with axes and legend
401 GridColour = '#A64910'
402 gridPen = QtGui.QPen()
403 gridPen.setColor(QtGui.QColor(GridColour))
404 gridPen.setWidth(int(self.
zoom))
406 for i
in range(NbMailles+1):
414 gridPen.setWidth(int(self.
zoom * 2))
419 PSize = int((13 - 2 * int(log(max(self.
scaleX, self.
scaleY), 10))))
420 for i
in range(NbMailles+1):
422 self.addSimpleText(
QString(str(int(i*self.
scaleX//NbMailles)).center(3)),
423 QtGui.QFont(
QString(
"Arial"), PSize)).setPos(QtCore.QPointF(self.
LeftMargin+i*mailleX-int(self.
zoom*12),
426 self.addSimpleText(
QString(str(int(i*self.
scaleY//NbMailles)).rjust(3)),
434 QtGui.QPen(QtGui.QColor(C.colour)), QtGui.QBrush(QtGui.QColor(C.colour), QtCore.Qt.SolidPattern))
438 def plot(self, Curve_id, newpoint, Width=3):
439 """ A version of PLOT that checks whether the new segment
440 remains within the frame. If not, the scale
is changed
441 and all curves are replotted
443 if self.
reframe(newpoint, Anticipation=
True):
445 Draw_Area.plot(self, Curve_id, newpoint, Width=Width)
452 """ Defines a 2-D region where agents are located and may move
455 DEFAULTSHAPE =
'ellipse'
458 def __init__(self, image=None, width=400, height=300, legend=True, EventInterpreter=None, zoom=1):
459 """ Creates a Draw_Area and initializes agents
464 Draw_Area.__init__(self, image, width, height, EventInterpreter, zoom=zoom)
475 """ if toric, edges 'touch' each other
480 """ Writing maximal values for both axes
491 """ Coord is a tuple.
492 It has the following form:
493 (x, y, colour, size, ToX, ToY, segmentColour, segmentThickness, 'shape=<form>')
494 (shorter tuples are automatically continued
with default values -
'shape=...' can be inserted anywhere)
495 The effect
is that an object of size
'size' is drawn at location (x,y) (your coordinates,
not pixels)
496 and a segment starting
from that blob
is drawn to (ToX, ToY) (
if these values are given)
507 if type(x) == str
and x.lower().startswith(
'shape'): Shape = x[x.rfind(
'=')+1:].strip()
509 Start =
Stroke(Coord1[:4], RefSize=self.
W)
510 if len(Coord1) > 4: End =
Stroke(Coord1[4:], RefSize=self.
W)
512 return (Start, End, Shape)
515 """ process modulo if toric and calls Draw_Area's convert
518 Coord = (Coord[0] % self.
scaleX, Coord[1] % self.
scaleY)
519 return Draw_Area.convert(self, Coord)
522 """ creates a dot at some location that represents an agent
526 def move_agent(self, Name, Coord=None, Position=None, Segment=None, Shape=None):
527 """ moves an agent's representative dot to some new location
529 def physical_move(AgentRef, Location):
530 """ moves a shape to a location
533 AgentRef.setPos(QtCore.QPointF(Location[0],Location[1]))
537 if Coord: (Position, Segment, Shape) = self.
coordinates(Coord)
538 else: Coord = Position.point() + Segment.point() + ((
'shape=%s' % Shape,)
if Shape
else ())
544 if self.
positions[Name].colour != Position.colour
or self.
shapes.get(Name) != Shape:
548 if str(Position.colour).startswith(
'-'):
552 if self.
positions[Name].Coord == Position.Coord \
553 and self.
segments[Name].Coord == Segment.Coord \
554 and self.
shapes.get(Name) == Shape:
562 physical_move(AgentRef, Location)
567 else: SegmentRef =
None
573 if str(Position.colour).startswith(
'-'):
578 physical_move(AgentRef, Location)
582 else: SegmentRef =
None
584 error(
"Draw_Area: unknown colour ID")
591 """ creates a graphic agent and returns the Q-reference
594 if Position.PixelSize:
595 PhysicalW = PhysicalH = Position.size
597 PhysicalW, PhysicalH = self.
xy2pixel((Position.size, Position.size))
598 if Shape ==
'ellipse':
599 return self.addEllipse(0, -PhysicalH, PhysicalW, PhysicalH,
601 QtGui.QBrush(QtGui.QColor(
EvolifeColourID(Position.colour)[1]), QtCore.Qt.SolidPattern))
602 if Shape ==
'rectangle':
603 return self.addRect(0, -PhysicalH, PhysicalW, PhysicalH,
605 QtGui.QBrush(QtGui.QColor(
EvolifeColourID(Position.colour)[1]), QtCore.Qt.SolidPattern))
607 if os.path.exists(Shape):
612 if Position.colour
and type(Position.colour) == int:
613 agent = self.addPixmap(self.
KnownShapes[Shape].transformed(QtGui.QTransform().rotate(-Position.colour)))
617 scaleW = float(PhysicalW) / agent.boundingRect().width()
618 scaleH = float(PhysicalH) / agent.boundingRect().width()
619 agent.setTransform(QtGui.QTransform.fromScale(scaleW, scaleH))
621 error(
"Ground: unknown shape: %s" % Shape)
625 """ creates a graphic segment and returns the Q-reference
628 self.Pens[Colour].setWidth(Segment.size)
631 return self.addLine(Location1[0], Location1[1], Location2[0], Location2[1], self.
Pens[Colour])
634 """ removes an agent from the ground
642 """ removes a segment from the ground
649 """ Returns the identificiation of all agents on the ground
654 """ removes agents that are not in Present
660 """ move agents vertically (not yet used)
667 """ draws a blob and a segment, as for an agent,
668 but without agent name and without moving
and removing options
670 (Position, Segment, Shape) = self.coordinates(Coord)
677 self.
move(Segment.colour, Position.point())
678 self.
plot(Segment.colour, Segment.point(), Width=Segment.size)
680 self.
move(Segment.colour, Segment.point())
681 self.speck(Segment.colour, Segment.point(), Size=0)
682 self.speck(Position.colour, Position.point(), Size=Position.size)
685 """ removes agents and erases Draw_Area
689 Draw_Area.erase(self)
693 """ the whole picture is redrawn when the scale changes
696 Draw_Area.redraw(self)
702 """ display an agent that is still present in 'positions' and in 'segments', but is graphically dead
705 Shape=self.shapes.get(Name))
713if __name__ ==
"__main__":
718__author__ =
'Dessalles'
Stores a list of 'Curves'.
def Legend(self)
returns tuples (ID, colour, colourname, curvename, legend) representing active curves
def CurveAddPoint(self, Curve_id, Point, Draw=True)
Adds a point to a Curve.
Stroke: drawing element (point or segment) #.
Draw_Area: Basic drawing Area.
def move(self, Curve_designation, newpoint)
introduces a discontinuity in a curve
def __init__(self, image=None, width=400, height=400, EventInterpreter=None, zoom=1)
Calls Image_Area constructor Initializes Curves.
def pixel2xy(self, Point)
converts physical coordinates into logical pixel(between 0 and scaleX)
def grid(self)
Initial display - to be overloaded.
def init_Pens(self)
Defining one pen and one QPainterPath per curve.
def set_margins(self, Left, Right, Bottom, Top)
surrounding margins (in Image_Area pixels)
def convert(self, Point)
Conversion of logical coordinates into physical coordinates through scaling and margin translation.
def xy2pixel(self, Point)
converts logical pixel (between 0 and scaleX) into physical coordinates through rescaling
def Q_Convert(self, Point)
Conversion of Point into Qt point.
def erase(self)
erase curves, items and restore grid
def drawTo(self, newpoint, Width=3, ColorID=0, Drawing=True, Tag='')
draws an additional segment on a given curve (from the end of the previous one)
def redraw(self)
the whole picture is redrawn when the scale changes
def plot(self, Curve_designation, newpoint, Width=3)
draws an additional segment on a curve
def reframe(self, Point, Anticipation=False)
performs a change of scale when necessary
def draw(self, oldpoint, newpoint, Width=3, ColorID=0, Tag='')
adds a segment on a given curve
Defines a 2-D region where agents are located and may move.
def convert(self, Coord)
process modulo if toric and calls Draw_Area's convert
def setToric(self, Toric=True)
if toric, edges 'touch' each other
def remove_agent(self, Name)
removes an agent from the ground
def grid(self)
Writing maximal values for both axes.
def erase(self)
removes agents and erases Draw_Area
def move_agent(self, Name, Coord=None, Position=None, Segment=None, Shape=None)
moves an agent's representative dot to some new location
def draw_tailed_blob(self, Coord)
draws a blob and a segment, as for an agent, but without agent name and without moving and removing o...
def __init__(self, image=None, width=400, height=300, legend=True, EventInterpreter=None, zoom=1)
Creates a Draw_Area and initializes agents.
def on_ground(self)
Returns the identificiation of all agents on the ground.
def remove_segment(self, Name)
removes a segment from the ground
def remove_absent(self, Present)
removes agents that are not in Present
def create_graphic_agent(self, Position, Shape=None)
creates a graphic agent and returns the Q-reference
def create_agent(self, Name, Coord)
creates a dot at some location that represents an agent
def scroll(self)
move agents vertically (not yet used)
def create_graphic_segment(self, Position, Segment)
creates a graphic segment and returns the Q-reference
def redraw(self)
the whole picture is redrawn when the scale changes
def restore_agent(self, Name)
display an agent that is still present in 'positions' and in 'segments', but is graphically dead
def coordinates(self, Coord)
Coord is a tuple.
Basic graphic area type #.
def dimension()
returns dimensions
def redraw(self)
the whole scene is redrawn when the scale changes
def resize(self, w, h)
Stores new dimensions and redraws.
def __init__(self, image=None, width=1000, height=1000, EventInterpreter=None, zoom=1)
Inherits from QGraphicsScene.
def drawPoints(self)
Unused ---— just for test.
Graphic area for displaying curves #.
def plot(self, Curve_id, newpoint, Width=3)
A version of PLOT that checks whether the new segment remains within the frame.
def __init__(self, image=None, width=556, height=390, EventInterpreter=None, zoom=1)
Calls Draw_Area constructor and sets margins.
def grid(self)
Drawing a grid with axes and legend
def display(self)
calling 'display' for all individuals in the population
Stores data that can be used to plot curves and stored into a file.
def EvolifeColourID(Colour_designation, default=(4, 'red'))
Recognizes Colour_designation as a number, a name, a (R,V,B) tuple or a #RRVVBB pattern.
def mouseLocate(self, MouseEvent)
convert mouse position into window coordinates
def mousePressEvent(self, MouseEvent)
retrieves mouse clicks - added by Gauthier Tallec
def speck(self, Curve_id, newpoint, Size=3)
draws a spot
def mouseReleaseEvent(self, MouseEvent)
retrieves mouse clicks
def mouseMoveEvent(self, MouseEvent)
retrieves mouse movements when button is pressed
def mouseDoubleClickEvent(self, MouseEvent)
retrieves mouse clicks