Skip to content

Screen

The Screen class is the backbone of the Phanim library and represents the windows where are animation are played.

Parameters:

Name Type Description Default
resolution tuple

The resolution of the screen in pixels.

None
zoom float

The initial zoom level of the camera.

6
fullscreen bool

Whether the screen should be displayed in fullscreen mode.

True
background tuple

The background color of the screen in RGB format.

(10, 15, 20)
fontSize float

The font size for text rendering.

0.5
panning bool

Whether the camera should allow panning.

True
renderer str

The rendering engine to use (e.g. "pygame", "moderngl").

'pygame'
grid bool

Whether to display a grid on the screen.

True
gridResolution int

The resolution of the grid.

15
gridBrightness int

The brightness of the grid lines.

150

Attributes:

Name Type Description
resolution tuple

The resolution of the screen in pixels.

camera Camera

The camera object managing the screen's view.

fontSize int

The font size for text rendering in pixels.

renderer Renderer

The rendering engine instance.

rendererName str

The name of the rendering engine.

record bool

Whether the screen is being recorded.

recording_output str

The output file for the screen recording.

recording_fps int

The FPS for the screen recording.

Source code in src/phanim/screen.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
class Screen():

    """
    The Screen class is the backbone of the Phanim library and represents the windows where are animation are played.

    Args:
        resolution (tuple): The resolution of the screen in pixels.
        zoom (float): The initial zoom level of the camera.
        fullscreen (bool): Whether the screen should be displayed in fullscreen mode.
        background (tuple): The background color of the screen in RGB format.
        fontSize (float): The font size for text rendering.
        panning (bool): Whether the camera should allow panning.
        renderer (str): The rendering engine to use (e.g. "pygame", "moderngl").
        grid (bool): Whether to display a grid on the screen.
        gridResolution (int): The resolution of the grid.
        gridBrightness (int): The brightness of the grid lines.

    Attributes:
        resolution (tuple): The resolution of the screen in pixels.
        camera (Camera): The camera object managing the screen's view.
        fontSize (int): The font size for text rendering in pixels.
        renderer (Renderer): The rendering engine instance.
        rendererName (str): The name of the rendering engine.
        record (bool): Whether the screen is being recorded.
        recording_output (str): The output file for the screen recording.
        recording_fps (int): The FPS for the screen recording.
    """

    def __init__(self, resolution=None, zoom=6, fullscreen=True, background=(10, 15, 20), fontSize=0.5, panning=True, renderer="pygame", grid=True, gridResolution=15, gridBrightness=150, record=False, recording_output="recording.mp4", recording_fps=60):

        if renderer == "pygame":
            self.renderer = PygameRenderer(resolution, fontSize, fullscreen, record=record,
                                           recording_output=recording_output, recording_fps=recording_fps)
        elif renderer == "moderngl":
            self.renderer = ModernGLRenderer(resolution, fontSize, fullscreen)
        else:
            raise "Render engine not found!"
        self.rendererName = renderer

        self.resolution = self.renderer.resolution

        self.camera = Camera(zoom, self.resolution)
        self.static_camera = Camera(zoom, self.resolution)

        self.fontSize = int(fontSize*self.camera.pixelsPerUnit)

        self.record = record

        # Setting static settings
        self.panning = panning
        self.background = background
        self.mouseThightness = 0.4

        # preparing lists
        self.updaterList = []
        self.mouseClickUpdaterList = []
        self.mouseDragUpdaterList = []
        self.mouseDownUpdaterList = []
        self.interativityList = []
        self.animationQueue = []
        self.drawList = []
        self.selectedObjects = []

        # presetting dynamic variables
        self.dragging = False
        self.mouseButtonDown = False
        self.t0 = time.time()
        self.scroll = [0, 0]
        self.lastScroll = [0, 0]
        self.grid = grid
        self.gridResolution = gridResolution
        self.gridBrightness = gridBrightness

        self.frameDt = 1/recording_fps

    def addUpdater(self, someFunction, substeps=1):
        """
        This method is called to add an updater. I.E. a function that updates every frame.

        Args:
            someFunction (function): The function to be called when the mouse button is released.
        """
        self.updaterList.append([someFunction, substeps])

    def addMouseClickUpdater(self, someFunction):
        """
        Similar to addUpdater, but only called when the mouse is released.

        Args:
            someFunction (function): The function to be called when the mouse button is released.
        """
        self.mouseClickUpdaterList.append(someFunction)

    def addMouseDownUpdater(self, someFunction):
        """
        Similar to addUpdater, but only called when the mouse button is pressed down.

        Args:
            someFunction (function): The function to be called when the mouse button is pressed down.
        """
        self.mouseDownUpdaterList.append(someFunction)

    def addMouseDragUpdater(self, someFunction):
        """
        Similar to addUpdater, but only called when the mouse button is being dragged.

        Args:
            someFunction (function): The function to be called when the mouse button is being dragged.
        """
        self.mouseDragUpdaterList.append(someFunction)

    def __handlePanning(self, mouseDown, dragging):
        if len(self.selectedObjects) > 0:
            dragging = False
        if mouseDown:
            self.panBeginCameraPosition = self.camera.position
            self.panBeginMousePos = self.LocalCursorPosition
        if dragging:
            self.camera.setPosition(
                (self.panBeginMousePos-self.LocalCursorPosition)+(self.panBeginCameraPosition))

        scroll = self.scroll
        scroll[0] /= 30
        scroll[1] /= 30
        self.camera.setZoom(self.camera.zoom - self.camera.zoom*scroll[1])

    def drawLines(self, lines, width, position, static=False):
        """
        Draws a list of lines on the screen.

        Args:
            lines (list): A list of lines, where each line is a list of two or three elements.
                The first two elements are the start and stop coordinates of the line.
                The third element is optional and specifies the color of the line.
            width (float): The width of the lines.
            position (list): The position offset of the lines.
        Returns:
            None
        """

        if static:
            cam = self.static_camera
        else:
            cam = self.camera

        for line in lines:
            start = cam.coords2screen(line[0] + position)
            stop = cam.coords2screen(line[1] + position)

            pixelWidth = int(width/cam.zoom*20)
            if pixelWidth < 1:
                pixelWidth = 1.5

            if len(line) == 3:
                color = line[2]
            else:
                color = (255, 255, 255)
            self.renderer.drawLine(color, start, stop, pixelWidth)

    def __drawCircles(self, circles, position, static=False):
        if static:
            cam = self.static_camera
        else:
            cam = self.camera

        for circle in circles:
            pos = cam.coords2screen(circle[1]+position)
            self.renderer.drawCircle(
                circle[2], pos, circle[0]*cam.pixelsPerUnit)

    def __drawPolygons(self, polygons, color, position, static=False):
        if static:
            cam = self.static_camera
        else:
            cam = self.camera

        for polygon in polygons:
            points = []
            for point in polygon:
                points.append(cam.coords2screen(point+position))
            self.renderer.drawPolygon(color, points)

    def __drawTexts(self, texts, position, static=False):
        if static:
            cam = self.static_camera
        else:
            cam = self.camera

        for text in texts:
            position = cam.coords2screen(position)
            color = text[2]
            size = text[1]
            text = text[0]
            size = int(size/cam.zoom*20)

            # We cap size for performance reasons
            if size < 1000:
                self.renderer.drawText(color, text, position, size)

    def __drawPhobject(self, phobject, static=False):
        if hasattr(phobject, 'circles'):
            self.__drawCircles(
                phobject.circles, phobject.position, static=static)
        if hasattr(phobject, 'lines'):
            self.drawLines(phobject.lines, phobject.lineWidth,
                           phobject.position, static=static)
        if hasattr(phobject, "polygons"):
            self.__drawPolygons(phobject.polygons,
                                phobject.color, phobject.position, static=static)
        if hasattr(phobject, "texts"):
            self.__drawTexts(phobject.texts, phobject.position, static=static)

    def draw(self, *args, static=False):
        """
        Draws the given phobjects or groups of phobjects on the screen.

        Parameters:
            *args: variable number of phobjects or groups of phobjects to be drawn

        Returns:
            None
        """
        for arg in args:

            if hasattr(arg, "static"):
                if arg.static:
                    static = True

            if hasattr(arg, "groupObjects"):
                for phobject in arg.groupObjects:
                    self.draw(phobject, static=static)
            else:
                self.__drawPhobject(arg, static=static)

    def __drawGrid(self, spacing, color=(255, 255, 255)):
        group = Group()
        boundX = self.camera.bounds[0]
        boundY = self.camera.bounds[1]

        amountX = int(np.ceil((boundX[1]-boundX[0])/spacing))
        amountY = int(np.ceil((boundY[1]-boundY[0])/spacing))

        for i in range(amountX):
            group.add(Line(
                start=[np.ceil(boundX[0]/spacing)*spacing +
                       i*spacing, boundY[0]],
                stop=[np.ceil(boundX[0]/spacing)*spacing+i*spacing, boundY[1]],
                lineWidth=0,
                color=color
            )
            )
        for i in range(amountY):
            group.add(Line(
                start=[boundX[0], np.ceil(
                    boundY[0]/spacing)*spacing+i*spacing],
                stop=[boundX[1], np.ceil(boundY[0]/spacing)*spacing+i*spacing],
                lineWidth=0,
                color=color
            )
            )

        self.draw(group)

    def __createGrid(self):
        width = self.camera.bounds[0][1]-self.camera.bounds[0][0]
        closestPower, distance = round_to_power_of_2(width/self.gridResolution)
        if self.rendererName == "pygame":
            self.__drawGrid(closestPower/4, color=(255, 255, 255,
                            interp(0, self.gridBrightness/3, distance)))
            self.__drawGrid(closestPower/2, color=(255, 255, 255,
                            interp(self.gridBrightness/3, self.gridBrightness, distance)))
            self.__drawGrid(closestPower, color=(
                255, 255, 255, self.gridBrightness))
        elif self.rendererName == "moderngl":
            color1 = interp(0, self.gridBrightness/3, distance)
            color2 = interp(self.gridBrightness/3,
                            self.gridBrightness, distance)
            color3 = self.gridBrightness
            self.__drawGrid(closestPower/4, color=(color1, color1, color1))
            self.__drawGrid(closestPower/2, color=(color2, color2, color2))
            self.__drawGrid(closestPower, color=(color3, color3, color3))

    def makeInteractive(self, *args):
        """
        Makes the given phobjects interactive. (If the phobject has an ```updateInteractivity``` method)

        Args:
            *args: A variable number of phobjects or lists of phobjects to be made interactive.

        Returns:
            None
        """
        for arg in args:
            if type(arg) is list:
                for phobject in arg:
                    self.interativityList.append(phobject)
            else:
                self.interativityList.append(arg)

    def __handleInteractivity(self):
        self.dt = self.frameDt
        if not self.dragging:
            self.selectedObjects = []

            for phobject in self.interativityList:

                static = False
                if hasattr(phobject, "static"):
                    if phobject.static:
                        static = True

                if static:
                    cam = self.static_camera
                    mouse = self.StaticCursorPosition
                else:
                    cam = self.camera
                    mouse = self.GlobalCursorPosition

                if hasattr(phobject, "radius"):
                    if magnitude(phobject.position-(mouse+cam.position)) < phobject.radius:
                        self.selectedObjects.append(phobject)
                elif hasattr(phobject, "checkSelection"):
                    if phobject.checkSelection(self):
                        self.selectedObjects.append(phobject)
                elif hasattr(phobject, "groupObjects"):
                    for phobject2 in phobject.groupObjects:
                        if hasattr(phobject2, "radius"):
                            if magnitude(phobject2.position-(mouse+cam.position)) < phobject2.radius:
                                self.selectedObjects.append(phobject2)

        for phobject in self.interativityList:
            if hasattr(phobject, "updateInteractivity"):
                phobject.updateInteractivity(self)

    def __handleInput(self):
        if self.rendererName == "pygame":
            self.keys = pygame.key.get_pressed()
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.renderer.isRunning = False
                    print("Window closed!")
                if event.type == pygame.MOUSEBUTTONUP:
                    self.dragging = False
                    for func in self.mouseClickUpdaterList:
                        func(self)
                if event.type == pygame.MOUSEBUTTONDOWN:
                    self.dragging = True
                    self.mouseButtonDown = True
                    for func in self.mouseDownUpdaterList:
                        func(self)
                if event.type == pygame.MOUSEWHEEL:
                    self.scroll = [event.x, event.y]

        if self.rendererName == "moderngl":
            if self.renderer.BUTTONUP:
                self.dragging = False
                for func in self.mouseClickUpdaterList:
                    func(self)
            if self.renderer.BUTTONUP:
                self.dragging = True
                self.mouseButtonDown = True
                for func in self.mouseDownUpdaterList:
                    func(self)
            self.scroll = self.renderer.scroll

        if self.dragging:
            for func in self.mouseDragUpdaterList:
                func(self)

    def __resetDisplay(self):
        self.renderer.reset(self.background)

    def __calculateCursor(self):
        pos = self.renderer.getMousePos()
        self.cursorPositionScreen = pos
        self.LocalCursorPosition = self.camera.screen2cords(pos)
        self.GlobalCursorPosition = self.LocalCursorPosition+self.camera.position
        self.StaticCursorPosition = self.LocalCursorPosition / \
            self.camera.zoom * self.static_camera.zoom

    def __performUpdateList(self):
        if self.t > .1:
            for func in self.updaterList:
                for i in range(func[1]):
                    self.dt = self.frameDt/func[1]
                    func[0](self)

    def __drawCursor(self):
        radius = 10
        # color = (150, 150, 150, 120)
        color = (150, 150, 150)
        center = self.cursorPositionScreen
        # self.renderer.setCursor(color,center,radius)
        self.renderer.drawCircle(color, center, radius, segments=10)

    def play(self, *args):
        """
        Adds the given arguments to the animation queue, which is used to schedule animations to be played on the screen.

        Parameters:
            *args (Any): The arguments to be added to the animation queue. These can be any type of object that can be passed as arguments to the draw method.

        Returns:
            None
        """

        self.animationQueue.append(list(args))

    def wait(self, duration):
        """
        Pauses the animation for a specified duration.

        Parameters:
            duration (int): The duration of the pause in frames.

        Returns:
            None
        """
        self.play(Sleep(duration))

    def __playAnimations(self):
        if len(self.animationQueue) > 0:
            for index, animation in enumerate(self.animationQueue[0]):

                if animation.mode == "wrapper":
                    animation.currentFrame += 1
                    self.__drawWrapperAnimation(animation)
                    animation.updateAndPrint()

                    if animation.currentFrame == animation.duration:
                        for wrappedAnimation in animation.animations:
                            if wrappedAnimation.mode == "add":
                                self.add(wrappedAnimation.object)
                            if wrappedAnimation.mode == "remove":
                                self.remove(wrappedAnimation.object)

                else:
                    self.__drawAnimation(animation)

                if animation.currentFrame == animation.duration:
                    self.animationQueue[0].pop(index)

            if len(self.animationQueue[0]) == 0:
                self.animationQueue.pop(0)

    def __drawAnimation(self, animation):
        if animation.currentFrame == 0:
            if hasattr(animation, "object"):
                animation.oldPhobject = deepcopy(animation.object)

        animation.currentFrame += 1
        animation.updateAndPrint()

        if animation.mode == "add":
            self.draw(animation)
        if animation.currentFrame == animation.duration:
            if animation.mode == "add":
                self.add(animation.object)
            if animation.mode == "remove":
                self.remove(animation.object)

    def __drawWrapperAnimation(self, animation):
        for index, wrappedAnimation in enumerate(animation.animations):
            if wrappedAnimation.currentFrame == 0:
                if hasattr(wrappedAnimation, "object"):
                    wrappedAnimation.oldPhobject = copy(
                        wrappedAnimation.object)
            if wrappedAnimation.mode == "add":
                self.draw(wrappedAnimation)

    def add(self, phobject):
        """
        Adds a phobject to the draw list.

        Args:
            phobject (any): The phobject to be added to the draw list.

        Returns:
            None
        """
        self.drawList.append(phobject)

    def remove(self, phobject):
        """
        Removes a phobject from the draw list.

        Args:
            phobject (any): The phobject to be removed from the draw list.

        Returns:
            None
        """
        self.drawList.remove(phobject)

    def __drawDrawList(self):
        self.draw(*self.drawList)

    def run_interactive(self, globals):
        """
        Runs the screen in interactive mode, allowing for IPython input.
        When used, this method replaces Screen.run().

        Args:
            globals (dict): The global namespace to be used in the IPython session.

        Returns:
            None
        """
        from IPython import start_ipython

        def thread_loop():
            start_ipython(argv=[], user_ns=globals)

        print_thread = threading.Thread(target=thread_loop)
        print_thread.daemon = True
        print_thread.start()
        self.run()

    def run(self):
        """
        Runs the main loop of the screen, handling user input, rendering, and updating. Necessary for phanim to function.

        Returns:
            None
        """
        while self.renderer.running():
            self.t = time.time() - self.t0

            self.__handleInput()
            self.__calculateCursor()
            if self.grid:
                self.__createGrid()
            self.__drawDrawList()
            self.__playAnimations()
            if self.panning:
                self.__handlePanning(self.mouseButtonDown, self.dragging)
            self.__handleInteractivity()
            self.__performUpdateList()

            self.__drawCursor()
            self.renderer.update(self.background)

            if not self.record:
                self.frameDt = self.renderer.getFrameDeltaTime()
            self.mouseButtonDown = False  # because this should only be True for a single frame
            self.__debug()

        self.renderer.quit()

    def __debug(self):
        pass

add(phobject)

Adds a phobject to the draw list.

Parameters:

Name Type Description Default
phobject any

The phobject to be added to the draw list.

required

Returns:

Type Description

None

Source code in src/phanim/screen.py
def add(self, phobject):
    """
    Adds a phobject to the draw list.

    Args:
        phobject (any): The phobject to be added to the draw list.

    Returns:
        None
    """
    self.drawList.append(phobject)

addMouseClickUpdater(someFunction)

Similar to addUpdater, but only called when the mouse is released.

Parameters:

Name Type Description Default
someFunction function

The function to be called when the mouse button is released.

required
Source code in src/phanim/screen.py
def addMouseClickUpdater(self, someFunction):
    """
    Similar to addUpdater, but only called when the mouse is released.

    Args:
        someFunction (function): The function to be called when the mouse button is released.
    """
    self.mouseClickUpdaterList.append(someFunction)

addMouseDownUpdater(someFunction)

Similar to addUpdater, but only called when the mouse button is pressed down.

Parameters:

Name Type Description Default
someFunction function

The function to be called when the mouse button is pressed down.

required
Source code in src/phanim/screen.py
def addMouseDownUpdater(self, someFunction):
    """
    Similar to addUpdater, but only called when the mouse button is pressed down.

    Args:
        someFunction (function): The function to be called when the mouse button is pressed down.
    """
    self.mouseDownUpdaterList.append(someFunction)

addMouseDragUpdater(someFunction)

Similar to addUpdater, but only called when the mouse button is being dragged.

Parameters:

Name Type Description Default
someFunction function

The function to be called when the mouse button is being dragged.

required
Source code in src/phanim/screen.py
def addMouseDragUpdater(self, someFunction):
    """
    Similar to addUpdater, but only called when the mouse button is being dragged.

    Args:
        someFunction (function): The function to be called when the mouse button is being dragged.
    """
    self.mouseDragUpdaterList.append(someFunction)

addUpdater(someFunction, substeps=1)

This method is called to add an updater. I.E. a function that updates every frame.

Parameters:

Name Type Description Default
someFunction function

The function to be called when the mouse button is released.

required
Source code in src/phanim/screen.py
def addUpdater(self, someFunction, substeps=1):
    """
    This method is called to add an updater. I.E. a function that updates every frame.

    Args:
        someFunction (function): The function to be called when the mouse button is released.
    """
    self.updaterList.append([someFunction, substeps])

draw(*args, static=False)

Draws the given phobjects or groups of phobjects on the screen.

Parameters:

Name Type Description Default
*args

variable number of phobjects or groups of phobjects to be drawn

()

Returns:

Type Description

None

Source code in src/phanim/screen.py
def draw(self, *args, static=False):
    """
    Draws the given phobjects or groups of phobjects on the screen.

    Parameters:
        *args: variable number of phobjects or groups of phobjects to be drawn

    Returns:
        None
    """
    for arg in args:

        if hasattr(arg, "static"):
            if arg.static:
                static = True

        if hasattr(arg, "groupObjects"):
            for phobject in arg.groupObjects:
                self.draw(phobject, static=static)
        else:
            self.__drawPhobject(arg, static=static)

drawLines(lines, width, position, static=False)

Draws a list of lines on the screen.

Parameters:

Name Type Description Default
lines list

A list of lines, where each line is a list of two or three elements. The first two elements are the start and stop coordinates of the line. The third element is optional and specifies the color of the line.

required
width float

The width of the lines.

required
position list

The position offset of the lines.

required

Returns: None

Source code in src/phanim/screen.py
def drawLines(self, lines, width, position, static=False):
    """
    Draws a list of lines on the screen.

    Args:
        lines (list): A list of lines, where each line is a list of two or three elements.
            The first two elements are the start and stop coordinates of the line.
            The third element is optional and specifies the color of the line.
        width (float): The width of the lines.
        position (list): The position offset of the lines.
    Returns:
        None
    """

    if static:
        cam = self.static_camera
    else:
        cam = self.camera

    for line in lines:
        start = cam.coords2screen(line[0] + position)
        stop = cam.coords2screen(line[1] + position)

        pixelWidth = int(width/cam.zoom*20)
        if pixelWidth < 1:
            pixelWidth = 1.5

        if len(line) == 3:
            color = line[2]
        else:
            color = (255, 255, 255)
        self.renderer.drawLine(color, start, stop, pixelWidth)

makeInteractive(*args)

Makes the given phobjects interactive. (If the phobject has an updateInteractivity method)

Parameters:

Name Type Description Default
*args

A variable number of phobjects or lists of phobjects to be made interactive.

()

Returns:

Type Description

None

Source code in src/phanim/screen.py
def makeInteractive(self, *args):
    """
    Makes the given phobjects interactive. (If the phobject has an ```updateInteractivity``` method)

    Args:
        *args: A variable number of phobjects or lists of phobjects to be made interactive.

    Returns:
        None
    """
    for arg in args:
        if type(arg) is list:
            for phobject in arg:
                self.interativityList.append(phobject)
        else:
            self.interativityList.append(arg)

play(*args)

Adds the given arguments to the animation queue, which is used to schedule animations to be played on the screen.

Parameters:

Name Type Description Default
*args Any

The arguments to be added to the animation queue. These can be any type of object that can be passed as arguments to the draw method.

()

Returns:

Type Description

None

Source code in src/phanim/screen.py
def play(self, *args):
    """
    Adds the given arguments to the animation queue, which is used to schedule animations to be played on the screen.

    Parameters:
        *args (Any): The arguments to be added to the animation queue. These can be any type of object that can be passed as arguments to the draw method.

    Returns:
        None
    """

    self.animationQueue.append(list(args))

remove(phobject)

Removes a phobject from the draw list.

Parameters:

Name Type Description Default
phobject any

The phobject to be removed from the draw list.

required

Returns:

Type Description

None

Source code in src/phanim/screen.py
def remove(self, phobject):
    """
    Removes a phobject from the draw list.

    Args:
        phobject (any): The phobject to be removed from the draw list.

    Returns:
        None
    """
    self.drawList.remove(phobject)

run()

Runs the main loop of the screen, handling user input, rendering, and updating. Necessary for phanim to function.

Returns:

Type Description

None

Source code in src/phanim/screen.py
def run(self):
    """
    Runs the main loop of the screen, handling user input, rendering, and updating. Necessary for phanim to function.

    Returns:
        None
    """
    while self.renderer.running():
        self.t = time.time() - self.t0

        self.__handleInput()
        self.__calculateCursor()
        if self.grid:
            self.__createGrid()
        self.__drawDrawList()
        self.__playAnimations()
        if self.panning:
            self.__handlePanning(self.mouseButtonDown, self.dragging)
        self.__handleInteractivity()
        self.__performUpdateList()

        self.__drawCursor()
        self.renderer.update(self.background)

        if not self.record:
            self.frameDt = self.renderer.getFrameDeltaTime()
        self.mouseButtonDown = False  # because this should only be True for a single frame
        self.__debug()

    self.renderer.quit()

run_interactive(globals)

Runs the screen in interactive mode, allowing for IPython input. When used, this method replaces Screen.run().

Parameters:

Name Type Description Default
globals dict

The global namespace to be used in the IPython session.

required

Returns:

Type Description

None

Source code in src/phanim/screen.py
def run_interactive(self, globals):
    """
    Runs the screen in interactive mode, allowing for IPython input.
    When used, this method replaces Screen.run().

    Args:
        globals (dict): The global namespace to be used in the IPython session.

    Returns:
        None
    """
    from IPython import start_ipython

    def thread_loop():
        start_ipython(argv=[], user_ns=globals)

    print_thread = threading.Thread(target=thread_loop)
    print_thread.daemon = True
    print_thread.start()
    self.run()

wait(duration)

Pauses the animation for a specified duration.

Parameters:

Name Type Description Default
duration int

The duration of the pause in frames.

required

Returns:

Type Description

None

Source code in src/phanim/screen.py
def wait(self, duration):
    """
    Pauses the animation for a specified duration.

    Parameters:
        duration (int): The duration of the pause in frames.

    Returns:
        None
    """
    self.play(Sleep(duration))