PathDemo
========

* :download:`Download example <PyObjCExample-PathDemo.zip>`

A PyObjC Example without documentation

.. rst-class:: tabber

Sources
-------

.. rst-class:: tabbertab

DemoView.py
...........

.. sourcecode:: python

    import random
    from math import pi as PI
    
    import Cocoa
    import objc
    import Quartz
    from objc import super  # noqa: A004
    
    
    class DemoView(Cocoa.NSView):
        _demoNumber = objc.ivar(type=objc._C_INT)
    
        def initWithFrame_(self, frameRect):
            self = super().initWithFrame_(frameRect)
            if self is None:
                return None
    
            self._demoNumber = 0
            return self
    
        def drawRect_(self, rect):
            context = Cocoa.NSGraphicsContext.currentContext().graphicsPort()
    
            Quartz.CGContextSetGrayFillColor(context, 1.0, 1.0)
            Quartz.CGContextFillRect(context, rect)
    
            if self._demoNumber == 0:
                rectangles(context, rect)
    
            elif self._demoNumber == 1:
                circles(context, rect)
    
            elif self._demoNumber == 2:
                bezierPaths(context, rect)
    
            elif self._demoNumber == 3:
                circleClipping(context, rect)
    
            else:
                Cocoa.NSLog("Invalid demo number.")
    
        def setDemoNumber_(self, number):
            self._demoNumber = number
    
    
    # The various demo functions
    
    
    def setRandomFillColor(context):
        Quartz.CGContextSetRGBFillColor(
            context,
            random.uniform(0, 1),
            random.uniform(0, 1),
            random.uniform(0, 1),
            random.uniform(0, 1),
        )
    
    
    def setRandomStrokeColor(context):
        Quartz.CGContextSetRGBStrokeColor(
            context,
            random.uniform(0, 1),
            random.uniform(0, 1),
            random.uniform(0, 1),
            random.uniform(0, 1),
        )
    
    
    def randomPointInRect(rect):
        return Quartz.CGPoint(
            x=random.uniform(Quartz.CGRectGetMinX(rect), Quartz.CGRectGetMaxX(rect)),
            y=random.uniform(Quartz.CGRectGetMinY(rect), Quartz.CGRectGetMaxY(rect)),
        )
    
    
    def randomRectInRect(rect):
        p = randomPointInRect(rect)
        q = randomPointInRect(rect)
        return Quartz.CGRectMake(p.x, p.y, q.x - p.x, q.y - p.y)
    
    
    def rectangles(context, rect):
        # Draw random rectangles (some stroked, some filled).
    
        for k in range(20):
            if k % 2 == 0:
                setRandomFillColor(context)
                Quartz.CGContextFillRect(context, randomRectInRect(rect))
    
            else:
                setRandomStrokeColor(context)
                Quartz.CGContextSetLineWidth(context, 2 + random.randint(0, 10))
                Quartz.CGContextStrokeRect(context, randomRectInRect(rect))
    
    
    def circles(context, rect):
        # Draw random circles (some stroked, some filled).
    
        for k in range(20):
            r = randomRectInRect(rect)
            w = Quartz.CGRectGetWidth(r)
            h = Quartz.CGRectGetHeight(r)
            Quartz.CGContextBeginPath(context)
    
            if w < h:
                v = w
            else:
                v = h
    
            Quartz.CGContextAddArc(
                context,
                Quartz.CGRectGetMidX(r),
                Quartz.CGRectGetMidY(r),
                v,
                0,
                2 * PI,
                False,
            )
            Quartz.CGContextClosePath(context)
    
            if k % 2 == 0:
                setRandomFillColor(context)
                Quartz.CGContextFillPath(context)
    
            else:
                setRandomStrokeColor(context)
                Quartz.CGContextSetLineWidth(context, 2 + random.randint(0, 10))
                Quartz.CGContextStrokePath(context)
    
    
    def bezierPaths(context, rect):
        for k in range(20):
            numberOfSegments = 1 + random.randint(0, 8)
            Quartz.CGContextBeginPath(context)
            p = randomPointInRect(rect)
            Quartz.CGContextMoveToPoint(context, p.x, p.y)
            for j in range(numberOfSegments):
                p = randomPointInRect(rect)
    
                if j % 2 == 0:
                    Quartz.CGContextAddLineToPoint(context, p.x, p.y)
    
                else:
                    c1 = randomPointInRect(rect)
                    c2 = randomPointInRect(rect)
                    Quartz.CGContextAddCurveToPoint(
                        context, c1.x, c1.y, c2.x, c2.y, p.x, p.y
                    )
    
            if k % 2 == 0:
                setRandomFillColor(context)
                Quartz.CGContextClosePath(context)
                Quartz.CGContextFillPath(context)
    
            else:
                setRandomStrokeColor(context)
                Quartz.CGContextSetLineWidth(context, 2 + random.randint(0, 10))
                Quartz.CGContextStrokePath(context)
    
    
    def circleClipping(context, rect):
        # Draw a random path through a circular clip.
    
        w = Quartz.CGRectGetWidth(rect)
        h = Quartz.CGRectGetHeight(rect)
        Quartz.CGContextBeginPath(context)
        if w < h:
            v = w
        else:
            v = h
        Quartz.CGContextAddArc(
            context,
            Quartz.CGRectGetMidX(rect),
            Quartz.CGRectGetMidY(rect),
            v / 2,
            0,
            2 * PI,
            False,
        )
        Quartz.CGContextClosePath(context)
        Quartz.CGContextClip(context)
    
        # Draw something into the clip.
        bezierPaths(context, rect)
    
        # Draw a clip path on top as a black stroked circle.
        Quartz.CGContextBeginPath(context)
        Quartz.CGContextAddArc(
            context,
            Quartz.CGRectGetMidX(rect),
            Quartz.CGRectGetMidY(rect),
            v / 2,
            0,
            2 * PI,
            False,
        )
        Quartz.CGContextClosePath(context)
        Quartz.CGContextSetLineWidth(context, 1)
        Quartz.CGContextSetRGBStrokeColor(context, 0, 0, 0, 1)
        Quartz.CGContextStrokePath(context)

.. rst-class:: tabbertab

PathDemoController.py
.....................

.. sourcecode:: python

    import Cocoa
    import objc
    
    
    class PathDemoController(Cocoa.NSObject):
        button = objc.ivar()
        popup = objc.ivar()
        window = objc.ivar()
        demoView = objc.ivar()
    
        def awakeFromNib(self):
            #  Add the title of your new demo to the END of this array.
    
            titles = ["Rectangles", "Circles", "Bezier Paths", "Circle Clipping"]
    
            self.popup.removeAllItems()
    
            for t in titles:
                self.popup.addItemWithTitle_(t)
    
        @objc.IBAction
        def runAgain_(self, sender):
            self.select_(self)
    
        @objc.IBAction
        def select_(self, sender):
            self.demoView.setDemoNumber_(self.popup.indexOfSelectedItem())
            self.demoView.setNeedsDisplay_(True)
    
        def applicationShouldTerminateAfterLastWindowClosed_(self, application):
            return True

.. rst-class:: tabbertab

main.py
.......

.. sourcecode:: python

    import DemoView  # noqa: F401
    import PathDemoController  # noqa: F401
    from PyObjCTools import AppHelper
    
    AppHelper.runEventLoop()

.. rst-class:: tabbertab

setup.py
........

.. sourcecode:: python

    """
    Script for building the example.
    
    Usage:
        python3 setup.py py2app
    """
    from setuptools import setup
    
    setup(
        name="PathDemo",
        app=["main.py"],
        data_files=["English.lproj"],
        setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-Quartz"],
    )

