# Grady

- `Download example`

A PyObjC Example without documentation

## Sources

### AppDelegate.py

    import Cocoa
    import objc
    from MyWindowController import MyWindowController

    class AppDelegate(Cocoa.NSObject):
        myWindowController = objc.ivar()

        @objc.IBAction
        def newDocument_(self, sender):
            if self.myWindowController is None:
                self.myWindowController = MyWindowController.alloc().initWithWindowNibName_(
                    "TestWindow"
                )

            self.myWindowController.showWindow_(self)

        def applicationDidFinishLaunching_(self, notification):
            self.newDocument_(self)

        def validateMenuItem_(self, theMenuItem):
            enable = self.respondsToSelector_(theMenuItem.action())

            # disable "New" if the window is already up
            if theMenuItem.action() == b"newDocument:":
                if self.myWindowController.window().isKeyWindow():
                    enable = False

            return enable

### MyBaseGradientView.py

    import Cocoa
    import objc

    class MyBaseGradientView(Cocoa.NSView):
        myGradient = objc.ivar()
        myStartColor = objc.ivar()
        myEndColor = objc.ivar()

        forceColorChange = objc.ivar.bool()
        myAngle = objc.ivar.double()
        myIsRadial = objc.ivar.bool()
        myOffsetPt = objc.ivar.NSPoint()

        def resetGradient(self):
            if self.forceColorChange and self.myGradient is not None:
                self.myGradient = None

            if self.myGradient is None:
                self.myGradient = (
                    Cocoa.NSGradient.alloc().initWithStartingColor_endingColor_(
                        self.myStartColor, self.myEndColor
                    )
                )
                self.forceColorChange = False

        def setStartColor_(self, startColor):
            self.myStartColor = startColor
            self.forceColorChange = True
            self.setNeedsDisplay_(True)

        def setEndColor_(self, endColor):
            self.myEndColor = endColor
            self.forceColorChange = True
            self.setNeedsDisplay_(True)

        def setAngle_(self, angle):
            self.myAngle = angle
            self.setNeedsDisplay_(True)

        def setRadialDraw_(self, isRadial):
            self.myIsRadial = isRadial
            self.setNeedsDisplay_(True)

        def getRelativeCenterPositionFromEvent_(self, theEvent):
            curMousePt = self.convertPoint_fromView_(theEvent.locationInWindow(), None)
            pt = Cocoa.NSMakePoint(
                (curMousePt.x - Cocoa.NSMidX(self.bounds()))
                / (self.bounds().size.width / 2.0),
                (curMousePt.y - Cocoa.NSMidY(self.bounds()))
                / (self.bounds().size.height / 2.0),
            )
            return pt

        def mouseDown_(self, theEvent):
            if self.myIsRadial:
                self.myOffsetPt = self.getRelativeCenterPositionFromEvent_(theEvent)
                self.setNeedsDisplay_(True)

        def mouseDragged_(self, theEvent):
            if self.myIsRadial:
                self.myOffsetPt = self.getRelativeCenterPositionFromEvent_(theEvent)
                self.setNeedsDisplay_(True)

### MyBezierGradientView.py

    import Cocoa
    from MyBaseGradientView import MyBaseGradientView

    class MyBezierGradientView(MyBaseGradientView):
        def init(self):
            self = super(MyBaseGradientView, self).init()
            if self is None:
                return None

            self.myOffsetPt = Cocoa.NSMakePoint(0.0, 0.0)
            return self

        def drawRect_(self, rect):
            self.resetGradient()

            bezierPath = Cocoa.NSBezierPath.alloc().init()
            bezierPath.appendBezierPathWithOvalInRect_(rect)

            if self.myIsRadial:
                self.myGradient.drawInBezierPath_relativeCenterPosition_(
                    bezierPath, self.myOffsetPt
                )

            else:
                self.myGradient.drawInBezierPath_angle_(bezierPath, self.myAngle)

### MyRectGradientView.py

    import Cocoa
    from MyBaseGradientView import MyBaseGradientView

    class MyRectGradientView(MyBaseGradientView):
        def init(self):
            self = super().init()
            if self is None:
                return self

            self.myOffsetPt = Cocoa.NSMakePoint(0.0, 0.0)
            return self

        def drawRect_(self, rect):
            self.resetGradient()

            # if the "Radial Gradient" checkbox is turned on, draw using 'myOffsetPt'
            if self.myIsRadial:
                self.myGradient.drawInRect_relativeCenterPosition_(
                    self.bounds(), self.myOffsetPt
                )

            else:
                self.myGradient.drawInRect_angle_(self.bounds(), self.myAngle)

### MyWindowController.py

    import Cocoa
    import objc
    from objc import super  # noqa: A004

    class MyWindowController(Cocoa.NSWindowController):
        rectGradientView = objc.IBOutlet()
        bezierGradientView = objc.IBOutlet()

        startColorWell = objc.IBOutlet()
        endColorWell = objc.IBOutlet()
        angle = objc.IBOutlet()
        angleSlider = objc.IBOutlet()

        radialCheck = objc.IBOutlet()
        radialExplainText = objc.IBOutlet()

        def initWithPath_(self, newPath):
            return super().initWithWindowNibName_("TestWindow")

        def awakeFromNib(self):
            # make sure our angle text input keep the right format
            formatter = Cocoa.NSNumberFormatter.alloc().init()
            formatter.setNumberStyle_(Cocoa.NSNumberFormatterDecimalStyle)
            self.angle.cell().setFormatter_(formatter)

            # setup the initial start color
            self.rectGradientView.setStartColor_(Cocoa.NSColor.orangeColor())
            self.bezierGradientView.setStartColor_(Cocoa.NSColor.orangeColor())
            self.startColorWell.setColor_(Cocoa.NSColor.orangeColor())

            # setup the initial end color
            self.rectGradientView.setEndColor_(Cocoa.NSColor.blueColor())
            self.bezierGradientView.setEndColor_(Cocoa.NSColor.blueColor())
            self.endColorWell.setColor_(Cocoa.NSColor.blueColor())

            # setup the initial angle value
            self.rectGradientView.setAngle_(90.0)
            self.bezierGradientView.setAngle_(90.0)
            self.angle.setStringValue_("90.0")
            self.angleSlider.setFloatValue_(90.0)

        @objc.IBAction
        def swapColors_(self, sender):
            startColor = self.startColorWell.color()
            endColor = self.endColorWell.color()

            # change all our view's start and end colors
            self.rectGradientView.setStartColor_(endColor)
            self.rectGradientView.setEndColor_(startColor)

            self.bezierGradientView.setStartColor_(endColor)
            self.bezierGradientView.setEndColor_(startColor)

            # fix our color wells
            self.startColorWell.setColor_(endColor)
            self.endColorWell.setColor_(startColor)

        @objc.IBAction
        def startColor_(self, sender):
            newColor = sender.color()
            self.rectGradientView.setStartColor_(newColor)
            self.bezierGradientView.setStartColor_(newColor)

        @objc.IBAction
        def endColor_(self, sender):
            newColor = sender.color()
            self.rectGradientView.setEndColor_(newColor)
            self.bezierGradientView.setEndColor_(newColor)

        def controlTextDidEndEditing_(self, notification):
            theAngle = self.angle.floatValue()
            self.rectGradientView.setAngle_(theAngle)
            self.bezierGradientView.setAngle_(theAngle)

            theAngleDougle = self.angle.doubleValue()
            self.angleSlider.setDoubleValue_(theAngleDougle)
            self.angleSlider.setNeedsDisplay_(True)

        @objc.IBAction
        def angleSliderChange_(self, sender):
            angleValue = sender.floatValue()
            self.rectGradientView.setAngle_(angleValue)
            self.bezierGradientView.setAngle_(angleValue)
            self.angle.setDoubleValue_(angleValue)

        @objc.IBAction
        def radialDraw_(self, sender):
            self.rectGradientView.setRadialDraw_(sender.selectedCell().state())
            self.bezierGradientView.setRadialDraw_(sender.selectedCell().state())

            # angle factor does not relate to radial draws
            self.angleSlider.setEnabled_(not sender.selectedCell().state())
            self.angle.setEnabled_(not sender.selectedCell().state())

            # hide/show the explain text for radial gradients
            self.radialExplainText.setHidden_(not sender.selectedCell().state())

### main.py

    import AppDelegate  # noqa: F401
    import MyBaseGradientView  # noqa: F401
    import MyBezierGradientView  # noqa: F401
    import MyRectGradientView  # noqa: F401
    import MyWindowController  # noqa: F401
    from PyObjCTools import AppHelper

    AppHelper.runEventLoop()

### setup.py

    """
    Script for building the example.

    Usage:
        python3 setup.py py2app
    """

    from setuptools import setup

    setup(
        name="Grady",
        app=["main.py"],
        data_files=["English.lproj"],
        setup_requires=["py2app", "pyobjc-framework-Cocoa"],
    )