PixEdit
PixEdit is a layered desktop image editor. Install the package, then launch it with pix — the same pattern as a short console script opening a PyQt6 window.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e .
pix
pix photo.png opens a file. python -m pixedit is the same entry point.
The version sits on the right of the status bar, and Help → About PixEdit (the PixEdit menu on macOS) opens a short guide to file types, Save versus Export, the output folder, plugins, and startup logs.
What you can do
Open. PNG, JPEG, WebP, TIFF, BMP, and GIF. Multi-frame GIF and TIFF become one layer per frame; only the first frame is visible. Multi-page PDF is rendered with pypdfium2 (File → Open, then pick 72, 144, or 216 DPI). Each page is a layer.
File toolbar. A strip under the menu bar has Save, Open, Place, Export, Undo, Redo, and Show folder. Save, Open, Place, and Export run the same actions as the File menu. Undo and Redo run the same actions as the Edit menu. The buttons stay labeled Undo and Redo; the menu names the step, such as Undo Brush, and the button is grayed out when that stack is empty. Save (Ctrl+S) writes the .pix project, asking for a path the first time. Open (Ctrl+O) replaces the current document. Place (Ctrl+Shift+O) adds an image as new layers and keeps the document. Export (Ctrl+E) writes a flattened PNG, JPEG, or WebP. Those dialogs start in the output folder. That folder is Documents/output/PixEdit until you pick another one with File → Choose output folder…. Show folder opens it in the file manager. Choosing a new folder does not move files already saved somewhere else, and Save of a project that already has a path still writes that file.
Text. The Text tool (T) asks for the string. Font family, style, alignment, size, line spacing, and rotation are on the tool panel, and the color is the palette. Style lists the real cuts of that family, such as Regular, Bold, or Italic. Align is Left, Center, Right, or Justify. On more than one line, center and right move the shorter lines, and justify spreads the gaps on every line except the last. One line does not move, because the text is only as wide as the glyphs. Click an existing text layer to edit it. Scaling and rotating a text layer keep it editable. Flipping rasterizes it.
Brush and eraser. Size, hardness, and opacity. The options panel shows one stamp: the brush is the foreground color on a checkerboard, and the eraser is a hole in a solid chip. A small brush stays small; a brush larger than the swatch fills it, and the slider still shows the real size. A fast drag is filled in along the path, so the stroke stays continuous. The pointer is a circle the size of the brush (the eraser uses the same circle). [ and ] change the size and the circle and the swatch update. The eraser clears pixels on the active raster layer (color and alpha), or lifts a mask when a mask layer is active. A selection clips it, the same way it clips the brush. A stroke on a new or smaller layer grows a tight RGBA bitmap around the opaque pixels and places it with the layer's (x, y). It is not expanded to a full-canvas transparent sheet. Brushing past that layer's edge grows the bitmap. The background, an imported full-canvas image, and a flattened image keep their size.
Color. The foreground swatch sits under the tools. Its Opacity slider sets the alpha used by the brush, by shape fill and stroke, and by the fill tool. The brush tool's own Opacity slider multiplies with that. The Color tool (I) still picks a color from the flattened image when you click. Its panel adjusts the active layer: hue, saturation, brightness, contrast, highlight, and shade. The sliders preview together. Apply keeps that as one undo step, and Reset, Escape, switching tools, or selecting another layer puts the layer back. A selection limits the grade. Text is rasterized when you apply it.
Fill. Fill (G) flood-fills 4-connected pixels on the active layer. Each RGBA channel of a filled pixel is within the tolerance of the pixel you click. The fill also covers the soft inner edge of a brush stroke, so a closed outline does not keep a rim of the old background. Text is rasterized first, the same way the brush does it. A selection limits the fill to that intersection. Clicking outside the layer or the selection reports that in the status bar. Clicking empty pixels that run to the edge of the layer does not paint, so an open transparent area does not cover the canvas. The inside of a closed outline still fills.
Shapes. Shape (U) drags a rectangle, ellipse, or line. While the mouse is down, the canvas shows the shape that will be committed, including fill, stroke width, color, and opacity.
Mask. Draw a rectangle, ellipse, or freehand region (R). The effect is pixelate, blur, solid color, noise, desaturate, invert, dim, or sharpen, stored on a mask layer. Solid uses the foreground color and makes the covered area opaque. The amount slider's label and range follow the effect, and Solid hides it. The pixels underneath are not rewritten. The effect is applied while compositing, so you can hide the layer, change its opacity, or delete it and the original image comes back. Export and Flatten bake the effect.
Layers. New, rename (double-click the name), reorder, show/hide, opacity, and blend modes: Normal, Multiply, Screen, Overlay. Duplicate, delete, merge down, and flatten. Merge down bakes the upper layer into the one beneath it, including a mask, as one undo step. New layers start empty and content-sized. Masks stay document-sized, which keeps the effect aligned with the canvas. File → Place… or Layer → Place image… (Ctrl+Shift+O) adds a PNG, JPEG, WebP, TIFF, BMP, GIF, or PDF as new layers centered on the current document. The open document is not replaced. Undo removes the placed layers.
Selection. After you draw a selection, the status bar lists what you can do next. Edit, Select, and Layer all have Cut selection to new layer (Ctrl+X) and Copy selection to new layer (Ctrl+C). Delete or Edit → Clear selection contents clears the pixels. Select → Deselect, Edit → Deselect, Ctrl+D, and Esc (when you are not mid-drag) clear the selection. The toolbar groups Transform, Hand, Zoom, Select, and Color together, then the paint tools, then Crop and Mask, then Marker and other plugins.
Transforms. Transform (V) drags the active layer. Corner handles scale it (Shift locks aspect) and the handle above the box rotates it. The tool panel flips the active layer, turns it 90°, or opens Scale and Rotate. Layer menu: scale, rotate, flip horizontal, flip vertical. Image menu: rotate or flip the whole canvas, crop to the selection, and trim transparent edges. The Crop tool (C) crops the canvas when you release.
Selection, paint, and adjustments. Rectangular, elliptical, and freehand selections, plus a magic wand (contiguous pixels within a per-channel tolerance). Shift adds, Alt subtracts. The selection is a tint plus a marching outline. After you draw one, the status bar notes that brush, fill, shapes, and filters stay inside it. Delete or Backspace clears those pixels on the active raster layer (text is rasterized). That does not delete the layer — the layer panel's Delete button does. Ctrl+D deselects. Image → Crop to selection crops the canvas. Edit → Clear selection contents is the same clear. The Color tool (I) and rectangle / ellipse / line shapes (U). Brightness/contrast, levels, hue/saturation, desaturate, invert, blur, sharpen, and pixelate stay in the Image and Filter menus. No selection means the whole layer.
View. Scroll to zoom, Space or the Hand tool (H) to pan, rulers, Fit (Ctrl+0), and actual pixels (Ctrl+1).
History. Undo (Ctrl+Z) and redo (Ctrl+Shift+Z or Ctrl+Y). The same commands are Undo and Redo on the top bar.
Save and export
| Action | Result |
|---|---|
Save (Ctrl+S) |
A .pix project: a zip with manifest.json, PNG layer assets, optional masks, and the selection. |
Export (Ctrl+E) |
A flattened PNG, JPEG, or WebP. JPEG is composited on white. |
Opening a PNG does not overwrite that file on Save. Save writes a .pix project. Use Export for a flattened image.
A mask in a project stays a mask. The source layer's pixels in the file are the originals.
Keyboard
| Key | Action |
|---|---|
V B E T R M C G I U H Z |
Transform, brush, eraser, text, mask, select, crop, fill, color, shape, hand, zoom |
[ ] |
Smaller / larger brush |
Ctrl+N Ctrl+O |
New, open |
Ctrl+S Ctrl+Shift+S |
Save project, save as |
Ctrl+E |
Export flattened |
Ctrl+Z Ctrl+Y |
Undo, redo |
Ctrl+A Ctrl+D Esc Ctrl+Shift+I |
Select all, deselect (Ctrl+D or Esc), invert selection |
Ctrl+X Ctrl+C |
Cut or copy the selection onto a new layer |
Delete Backspace |
Clear pixels inside the selection |
Ctrl+Shift+O |
Place an image as new layers |
Ctrl+J Ctrl+Shift+N |
Duplicate layer, new layer |
Ctrl+Shift+E |
Merge down |
Ctrl+0 Ctrl+1 Ctrl++ Ctrl+- |
Fit, 100%, zoom in, zoom out |
Help → Keyboard shortcuts lists these inside the app. The left toolbar shows the same letter on each tool button, for example Brush (B). Marker is an example plugin and has no shortcut.
Plugins
Plugins register commands and canvas tools without editing the editor. register(api) is the only hook. PixEdit loads:
- The
pixedit.pluginsentry-point group. - The shipped example, if that entry point is not installed yet.
- Every
*.pyfile in~/.pixedit/plugins/.
The shipped example adds Filter → Vignette and a Marker tool. Marker is a stamp demo, not a second editing mode: each click draws a plus (and a small ring) on the active raster or text layer and the status bar says “Stamped marker”. There is no next step. Source: pixedit/plugins/examples/basic.py.
A user plugin is a single file:
# ~/.pixedit/plugins/invert_extra.py
from pixedit.model import MutateLayerCommand, apply_visual, visual_state
def invert_twice(session):
layer = session.active_layer()
if layer is None or layer.kind == "redaction":
session.show_message("Pick a raster or text layer.")
return
before = visual_state(layer)
image = layer.image.copy()
# ... edit `image`, then commit one undo step:
layer.image = image
layer.kind = "raster"
layer.text = None
new_state = visual_state(layer)
apply_visual(layer, before)
session.commit(MutateLayerCommand(layer, new_state, name="Invert twice"))
def register(api):
api.add_command(
command_id="mine.invert_twice",
label="Invert twice",
menu="Filter",
callback=invert_twice,
)
Restart, or choose Plugins → Reload plugins. A canvas tool subclasses pixedit.tools.base.Tool (id, label, pointer_down / pointer_move / pointer_up, optional option_schema) and is registered with api.add_tool(tool).
To ship a plugin inside another package:
[project.entry-points."pixedit.plugins"]
my_plugin = "my_package.module:register"
Plugins should edit the document through session.commit(...) so undo works. They do not need to import Qt. A plugin that fails to load is reported and skipped.
Layout
The document (canvas size, ordered layers, selection) lives in pixedit/model.py and is independent of Qt. pixedit/session.py applies undoable commands. pixedit/composite.py flattens the stack, including mask effects. Tools in pixedit/tools/ and the window in pixedit/ui/ call the session. They do not own the pixels.
Tests and headless use
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e .
pytest
pix --check
pytest covers compositing, the layer stack, blur and pixelate masks, transforms, paint, selection, project IO, and a headless window construct. pix --check builds the window on the offscreen Qt platform and exits.
pix itself needs a display (DISPLAY or WAYLAND_DISPLAY on Linux). On a machine without one it prints that and exits instead of crashing. Set QT_QPA_PLATFORM=offscreen only for the check. Startup failures are written under ~/.pixedit/logs/ (~/Library/Logs/pixedit on macOS).
Requirements
Python 3.10 or newer. PyQt6, Pillow, numpy, pypdfium2, and pytest are listed in both requirements.txt and pyproject.toml. Change a dependency in both files.
Metadata
Release files for pixedit 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pixedit-0.1.1.tar.gz | 126.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pixedit-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 243.0 kB
Release files / pixedit-0.1.1.tar.gz
| Download URL | pixedit-0.1.1.tar.gz |
|---|---|
| Size | 126.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6f5a3fbca1833d68d11fcadb60fa67eebd0129e56a66463287cdb4ebe47f07d4
|
|
BLAKE2b-256 checksum How to use checksums |
5bc781ff5b683e71eefcac40f83e5d1b852b0a3f7a2078c20264d8b112616511
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / pixedit-0.1.1-py3-none-any.whl
| Download URL | pixedit-0.1.1-py3-none-any.whl |
|---|---|
| Size | 116.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6c729ebd887630613dd4a229abdd23e4c3d2c73a333144d704c5d1f399e00600
|
|
BLAKE2b-256 checksum How to use checksums |
59cefb5ecdc9f4ad0918ba4f01356a789854848f1c71f4a8945eb28d3c0fd203
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for pixedit 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pixedit-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.