Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

pyDraw

version

pyDraw is a graphics library built to keep drawing and input simple and synchronized.

It started as a replacement for turtle in computer science classrooms. It has grown quite a bit since then, but the goal is still the same: make graphics easy to learn, easy to teach, and useful beyond the first lesson.

Features

  • Simple, one-line shape construction
  • Consistent object management and manipulation
  • Straightforward mouse and keyboard input
  • A top-left anchored coordinate system
  • Regular shapes and custom polygons
  • Precise .overlaps() and .contains() methods for Renderables
  • Dedicated Location (Vector2D) and Color classes
  • Support for tuples and Locations throughout the API, including constructors
  • Designed to be learned, taught, and extended

Getting Started

The easiest way to install pyDraw is from PyPI:

python -m pip install pydraw

Other Options

You can also download the single-file pydraw.py from the releases page and place it in your project's directory.

After installation, you can import the library with:

from pydraw import *

The wildcard import contains pyDraw's supported drawing API. Explicit imports work too, of course, if that better suits your project.

Basic Setup

After importing pydraw, you can write a basic skeleton program like so:

from pydraw import *

screen = Screen(800, 600, 'My First Project!')  # Create a screen to draw on.

screen.loop()  # Keep the program open and update the screen each frame.

We can create our first object with just one new line:

from pydraw import *

screen = Screen(800, 600, 'My First Project!')

# This creates a rectangle at x=50, y=50 that is 50 pixels wide and 50 pixels tall.
# Shapes are top-left anchored, so this position is the rectangle's top-left corner.
# The canvas origin is also at the top left: positive x goes right and positive y goes down.
box = Rectangle(screen, 50, 50, 50, 50)

screen.loop()

And getting straight to the point, one of pyDraw's primary features is easy user input:

from pydraw import *

screen = Screen(800, 600, 'My First Project!')

box = Rectangle(screen, 50, 50, 50, 50)

def mousedown(location, button):
    print(f'Wow, mouse button {button}!')

def mouseup(location, button):
    print('How un-impressive...')

def keydown(key):
    print(f'You pressed {key}. Keyboard input!')

def keyup(key):
    print('For when you really just gotta stop moving, keyup is here to save you.')

# All of the above methods must be defined above this statement (because Python):
screen.listen()


# As you can see, input is just a matter of defining functions and telling pyDraw to listen.
screen.loop()

Most objects use the same getter/setter style, so once you know one shape, the others feel familiar:

# ... code above

box = Rectangle(screen, 50, 50, 50, 50)  # Remember this is (x, y, width, height)!
box.x(box.y())  # Set the box's x coordinate to its y coordinate.
                # Calling box.x() without a value returns the current coordinate.

box.location()  # We can get its Location like this!

box.move(-5, 100)  # Move by -5 on the x-axis and 100 on the y-axis.
box.moveto(screen.width() / 2, screen.height() / 2)  # Move near the screen's center.

box.width(box.height())  # Set the box's width to its height.

box.color(Color('red'))  # Let's change the color to red.
screen.color(Color('lightblue'))  # The screen background can change too.

box.border(Color('black'), fill=False)  # Add a black border without filling the shape.

box.rotate(14)  # Rotate our box by 14 degrees clockwise.
box.rotation(box.rotation() + 14)  # The rotation method can get and set the angle too.

box.visible(False)  # Hide our box. Calling box.visible() returns its current state.

box.remove()  # Just get rid of that old box. We can make a better one soon :)

# code below ...

Lastly we can create some other objects and interact with them:

All the Renderables below support the common methods above, including overlaps() and contains(). CustomPolygon and Image have specialized constructors and a few extra operations, but they still share the same geometry and collision API.

# ... code above

not_a_box = Oval(screen, 400, 50, 100, 100, Color('magenta'))  # now we have a beautiful oval
almost_a_box = Triangle(screen, 200, 450, 100, 50, Color('yellow'), rotation=30)  # uno dos tres
# ^ Also note that we are setting the color, and also setting the rotation of the triangle,
# but the other parameters are still in the usual format: (x, y, width, height).
# IMPORTANT: Triangle's base is on the left, with the triangle's location as its top corner.

# We can create a regular polygon by specifying a number of sides before the location.
# The constructor is (screen, num_sides, x, y, width, height)!
schrodingers_box = Polygon(screen, 5, 250, 150, 50, 50, border=Color('red'))

# We can create an evil polygon like this (we can pass in a list of locations or tuples):
weird_evil_box = CustomPolygon(screen, [(500, 50), (550, 50), (550, 100), (500, 50)])
# ^ The real term for these is "irregular polygons." But irregular is hard to type,
#   so here we are.


# We can interact with these objects with these methods:
not_a_box.overlaps(almost_a_box)  # Do these objects overlap?
weird_evil_box.contains(Location(525, 75))  # Is this point inside the shape?

schrodingers_box.distance(not_a_box)  # Gets the precise distance between the centers

# code below ...

Text, Images, and Lines

Text, Image, and Line follow the same general rules as other objects, with a few operations of their own.

Text

# ... code above

text = Text(screen, 'Some Cool Text!', 0, 0, Color('purple'))
text.center(screen.width() / 2, screen.height() / 2)  # Centering text is easy!
text.font('Calibri')
text.rotate(45)  # You can still rotate text if you want :)

text.bold(True)
text.underline(True)
text.strikethrough(False)

# code below ...

Images

# ... code above

image = Image(screen, 'image.png', screen.width() / 3, screen.height() / 3)
# ^ We can load an image like this, and it will display on the screen.
# PNG, GIF, and PPM images can be displayed directly. Resizing, tinting, rotating,
# flipping, and other bitmap operations use Pillow. It is installed with the package;
# single-file users can install it with: python -m pip install pillow

# Now that we have Pillow installed, we can have some fun.
image.width(150)
image.height(150)

image.rotate(5)

image.color(Color('red'), alpha=123)
# ^ This will tint the image with a red color, at an alpha level of 123 (default).
# If you increase the alpha, the image will become less visible and the tint-color more so,
# and vice versa.

# code below ...

Lines

# ... code above

# Let's create a nice line that goes across the screen with a beautiful blue color.
line = Line(screen, 150, 150, screen.width() - 50, screen.height() - 50,
            color=Color('blue'))

# We can modify the line's thickness:
line.thickness(5)

# We can even rotate the line!
line.rotate(35, point=1)  # Note that here we are specifying which point to rotate AROUND!

# We can use a special feature of lines to make them point at stuff!
line.lookat(another_location)  # SUPER NIFTY!

# code below ...

Documentation

The documentation is available at pydraw.graphics. Documentation is also shipped with the package, so supporting IDEs can show completion and method details.


Extending pyDraw

The normal drawing API lives at the package root. If you are building a new platform backend, the lower-level contracts are kept in pydraw.runtime, pydraw.render, and pydraw.events. This keeps the beginner-facing API small without closing off the internals meant for extension.

Custom drawing objects can build on Object, Renderable, CustomRenderable, or CustomPolygon, depending on how much of pyDraw's geometry behavior they need.


DIY

If you want to build your own version of pyDraw, fork this repository and make it yours. Running python tools/compile.py from the repository root creates the single-file build at compiled/pydraw.py, where it will be held dearly.


License

pyDraw is available under the MIT License.

Third-party components and examples that carry their own license notices remain subject to those notices.


A Big Thanks To

  • Barry Lindler (An incredible person and a good friend)
  • Whatever geniuses came up with the crossing number algorithm, and the line-segment orientation algorithm

Metadata

Release files for pydraw 3.0a1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pydraw 3.0a1
File Size Uploaded
pydraw-3.0a1.tar.gz 74.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pydraw 3.0a1
File Interpreter ABI Platform
pydraw-3.0a1-py3-none-any.whl Python 3 none any Details

Total release size: 150.9 kB

Release files / pydraw-3.0a1.tar.gz

Download URL pydraw-3.0a1.tar.gz
Size 74.5 kB
Tags Source
SHA-256 checksum
How to use checksums
f69567576f20f8c0e69b99cc7ffeddbd1e6da8a2bf63dbc959150551cdcb1280
BLAKE2b-256 checksum
How to use checksums
77375b807fb27e04d7d8abfa9e48847988db487e44b448644cc6e5afe57b9339
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pydraw-3.0a1-py3-none-any.whl

Download URL pydraw-3.0a1-py3-none-any.whl
Size 76.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
de33ed6e7f11d35b9a9938b2adc1d54d163452a6bdefc3175d3672a36bac067a
BLAKE2b-256 checksum
How to use checksums
7d798a7acdd58d1f4420e60711201bb54b7d19a3a1367bdba6af81d2e1b6e303
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.31 {"installer":{"name":"uv","version":"0.11.31","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

3.0a1 This release

2 release files

2.2.1

1 release file

2.2

1 release file

2.1.5

1 release file

2.1.4

1 release file

2.1.3

1 release file

2.1.2

1 release file

2.1.1

2 release files

2.1

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0

2 release files

1.5

2 release files

1.4.1

2 release files

1.4

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.4

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.2

2 release files

0.0.1

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page