# CollidingMiceQt.py # 2011-10-02 File copied from Python Qt examples by Kari Laitinen # 2011-10-02 Last modification. # http://www.naturalprogramming.com # This file is a standard Qt example program whose Copyright # belongs to Trolltech (Nokia). # This file is modified by Kari Laitinen to better suit to # learning purposes. # This program demonstrates the use of QGraphicsItem, QGraphicsScene, # and QGraphicsView classes. # See notes at the end of this file. import math from PyQt4.QtCore import Qt, qsrand, qrand, qAbs, QTime, QTimer from PyQt4.QtCore import QPointF, QRectF, QLineF from PyQt4.QtGui import QGraphicsItem, QGraphicsScene, QGraphicsView from PyQt4.QtGui import QPainter, QBrush, QPixmap, QPainterPath, QColor, QPolygonF from PyQt4.QtGui import QApplication class Mouse( QGraphicsItem ) : Pi = math.pi TwoPi = 2.0 * Pi # Create the bounding rectangle once. adjust = 0.5 BoundingRect = QRectF(-20 - adjust, -22 - adjust, 40 + adjust, 83 + adjust) def __init__( self ) : super( Mouse, self ).__init__() self.mouse_angle = 0.0 self.mouse_speed = 0.0 self.mouse_eye_direction = 0.0 self.mouse_color = QColor( qrand() % 256, qrand() % 256, qrand() % 256 ) self.rotate( qrand() % (360 * 16) ) # In the C++ version of this example, this class is also derived from # QObject in order to receive timer events. PyQt does not support # deriving from more than one wrapped class so we just create an # explicit timer instead. self.timer = QTimer() self.timer.timeout.connect( self.timerEvent ) self.timer.start( 1000 / 33 ) @staticmethod def normalizeAngle(angle): while angle < 0: angle += Mouse.TwoPi while angle > Mouse.TwoPi: angle -= Mouse.TwoPi return angle # Methods boundingRect() and shape() are important as they may be used # for activities such as collision detection. def boundingRect( self ) : return Mouse.BoundingRect def shape( self ) : mouse_shape_path = QPainterPath() mouse_shape_path.addRect( -10, -20, 20, 40 ) return mouse_shape_path ; def paint( self, painter, option, widget ) : # Body. painter.setBrush( self.mouse_color ) painter.drawEllipse( -10, -20, 20, 40 ) # Eyes. painter.setBrush( Qt.white ) painter.drawEllipse( -10, -17, 8, 8 ) painter.drawEllipse( 2, -17, 8, 8 ) # Nose. painter.setBrush( Qt.black ) painter.drawEllipse( QRectF( -2, -22, 4, 4 ) ) # Pupils. painter.drawEllipse( QRectF( -8.0 + self.mouse_eye_direction, -17, 4, 4 ) ) painter.drawEllipse( QRectF( 4.0 + self.mouse_eye_direction, -17, 4, 4 ) ) # Ears. if self.scene().collidingItems( self ) : painter.setBrush( Qt.red ) else: painter.setBrush( Qt.darkYellow ) painter.drawEllipse(-17, -12, 16, 16) painter.drawEllipse(1, -12, 16, 16) # Tail. tail_path = QPainterPath( QPointF( 0, 20 ) ) tail_path.cubicTo(-5, 22, -5, 22, 0, 25) tail_path.cubicTo(5, 27, 5, 32, 0, 30) tail_path.cubicTo(-5, 32, -5, 42, 0, 35) painter.setBrush( Qt.NoBrush ) painter.drawPath( tail_path ) def timerEvent( self ) : # Don't move too far away. line_to_center = QLineF( QPointF(0, 0), self.mapFromScene( 0, 0 ) ) if line_to_center.length() > 150 : angle_to_center = math.acos( line_to_center.dx() / line_to_center.length() ) if line_to_center.dy() < 0: angle_to_center = Mouse.TwoPi - angle_to_center angle_to_center = Mouse.normalizeAngle( ( Mouse.Pi - angle_to_center) + Mouse.Pi / 2 ) if angle_to_center < Mouse.Pi and \ angle_to_center > Mouse.Pi / 4: # Rotate left. self.mouse_angle += [-0.25, 0.25][self.mouse_angle < -Mouse.Pi / 2] elif angle_to_center >= Mouse.Pi and \ angle_to_center < (Mouse.Pi + Mouse.Pi / 2 + Mouse.Pi / 4): # Rotate right. self.mouse_angle += [-0.25, 0.25][self.mouse_angle < Mouse.Pi / 2] elif math.sin( self.mouse_angle ) < 0 : self.mouse_angle += 0.25 elif math.sin( self.mouse_angle ) > 0 : self.mouse_angle -= 0.25 # Try not to crash with any other mice. # Method items() returns a list of GraphicsItem objects that are located # in the specified area on the screen. In this program the items are # other Mouse objects. dangerous_mice = self.scene().items( QPolygonF( [ self.mapToScene( 0, 0 ), self.mapToScene(-30, -50 ), self.mapToScene( 30, -50 ) ] ) ) for another_mouse in dangerous_mice : if another_mouse is self: continue line_to_another_mouse = QLineF( QPointF( 0, 0 ), self.mapFromItem( another_mouse, 0, 0 ) ) angle_to_another_mouse = math.acos( line_to_another_mouse.dx() / line_to_another_mouse.length() ) if line_to_another_mouse.dy() < 0 : angle_to_another_mouse = Mouse.TwoPi - angle_to_another_mouse angle_to_another_mouse = Mouse.normalizeAngle( ( Mouse.Pi - angle_to_another_mouse ) + Mouse.Pi / 2 ) if angle_to_another_mouse >= 0 and \ angle_to_another_mouse < Mouse.Pi / 2 : # Rotate right. self.mouse_angle += 0.5 elif angle_to_another_mouse <= Mouse.TwoPi and \ angle_to_another_mouse > (Mouse.TwoPi - Mouse.Pi / 2 ) : # Rotate left. self.mouse_angle -= 0.5 # Add some random movement. if len( dangerous_mice ) > 1 and ( qrand() % 10 ) == 0 : if qrand() % 1: self.mouse_angle += ( qrand() % 100 ) / 500.0 else: self.mouse_angle -= ( qrand() % 100) / 500.0 self.mouse_speed += (-50 + qrand() % 100 ) / 100.0 dx = math.sin( self.mouse_angle ) * 10 self.mouseEyeDirection = [dx / 5, 0.0][ qAbs(dx / 5) < 1] self.rotate( dx ) self.setPos( self.mapToParent( 0, -( 3 + math.sin( self.mouse_speed ) * 3 ) ) ) if __name__ == '__main__': import sys this_application = QApplication( sys.argv ) qsrand( QTime( 0, 0, 0).secsTo( QTime.currentTime() ) ) graphics_scene = QGraphicsScene() graphics_scene.setSceneRect( -300, -300, 600, 600 ) graphics_scene.setItemIndexMethod( QGraphicsScene.NoIndex ) number_of_mice_to_create = 7 for mouse_index in range( number_of_mice_to_create ) : new_mouse = Mouse() new_mouse.setPos( math.sin( ( mouse_index * 6.28 ) / number_of_mice_to_create ) * 200, math.cos( ( mouse_index * 6.28 ) / number_of_mice_to_create ) * 200 ) graphics_scene.addItem( new_mouse ) graphics_view = QGraphicsView( graphics_scene ) graphics_view.setRenderHint( QPainter.Antialiasing ) graphics_view.setBackgroundBrush( QBrush( QPixmap( "cheese.jpg" ) ) ) graphics_view.setCacheMode( QGraphicsView.CacheBackground ) graphics_view.setViewportUpdateMode( QGraphicsView.BoundingRectViewportUpdate ) graphics_view.setDragMode( QGraphicsView.ScrollHandDrag ) graphics_view.setWindowTitle( "Colliding Mice" ) graphics_view.resize(400, 300) graphics_view.show() sys.exit( this_application.exec_() ) # NOTES: # A QGraphicsView is an object for displaying the contents of a QGraphicsScene. # The QGraphicsScene class provides a surface for managing a large number of # 2D graphical items, which are GraphicsItem-based objects. # In the original version of this program the backgound is created # with the statement # view.setBackgroundBrush(QtGui.QBrush(QtGui.QPixmap(':/images/cheese.jpg'))) # which uses the resource file to create the image data. # When creating a QPixmap object, the file name # can either refer to an actual file on disk or to one of the application's # embedded resources. # All the 'standard' PyQt examples can be run with a command such as # C:\Python31\Lib\site-packages\PyQt4\examples\demos\qtdemo>python qtdemo.pyw