<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - coordinates</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/coordinates.tag.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2020-05-10T06:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>Understanding QPainter Coordinates in PyQt6 — How the coordinate system works for drawing on canvases in PyQt6</title><link href="https://www.pythonguis.com/faq/coordinates-on-qpainter/" rel="alternate"/><published>2020-05-10T06:00:00+00:00</published><updated>2020-05-10T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2020-05-10:/faq/coordinates-on-qpainter/</id><summary type="html">I really having trouble understanding the coordinate system used in &lt;code&gt;QPainter&lt;/code&gt;. Can you explain how this works?</summary><content type="html">
            &lt;blockquote&gt;
&lt;p&gt;I really having trouble understanding the coordinate system used in &lt;code&gt;QPainter&lt;/code&gt;. Can you explain how this works?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you've started drawing with &lt;code&gt;QPainter&lt;/code&gt; in PyQt6, you might have been surprised the first time you drew a line. You pass in coordinates like &lt;code&gt;(10, 10, 300, 200)&lt;/code&gt; and the result doesn't look quite like what you'd expect from a math class. That's because &lt;code&gt;QPainter&lt;/code&gt; uses a coordinate system where &lt;strong&gt;the origin (0, 0) is in the top-left corner&lt;/strong&gt; of the canvas, not the bottom-left.&lt;/p&gt;
&lt;p&gt;This catches a lot of people off guard, so in this tutorial we'll walk through exactly how QPainter coordinates work, how to visualize them, and how to convert between screen coordinates and the mathematical coordinate system you might be more familiar with.&lt;/p&gt;
&lt;h2 id="the-qpainter-coordinate-system"&gt;The QPainter coordinate system&lt;/h2&gt;
&lt;p&gt;In most math courses, you learn to plot points on a Cartesian plane where &lt;code&gt;(0, 0)&lt;/code&gt; is at the bottom-left. The x-axis increases to the right, and the y-axis increases upward.&lt;/p&gt;
&lt;p&gt;QPainter (and most screen-based graphics systems) does things differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(0, 0)&lt;/code&gt; is at the &lt;strong&gt;top-left&lt;/strong&gt; corner of the drawing surface.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;x-axis&lt;/strong&gt; increases to the &lt;strong&gt;right&lt;/strong&gt; (same as math).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;y-axis&lt;/strong&gt; increases &lt;strong&gt;downward&lt;/strong&gt; (opposite of math).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that as your y value gets larger, you move &lt;em&gt;down&lt;/em&gt; the screen, not up. Here's a simple diagram to illustrate:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;(0,0) &amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;&amp;boxh;► x increases
  &amp;boxv;
  &amp;boxv;
  &amp;boxv;
  &amp;boxv;
  ▼
  y increases
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So when you call &lt;code&gt;painter.drawLine(10, 10, 300, 200)&lt;/code&gt;, you're drawing a line from a point near the top-left corner down to a point further right and further &lt;em&gt;down&lt;/em&gt; the canvas.&lt;/p&gt;
&lt;h2 id="seeing-it-in-action"&gt;Seeing it in action&lt;/h2&gt;
&lt;p&gt;Let's draw a line and annotate the start and end points so you can see exactly where the coordinates land. This complete example creates a small window with a &lt;code&gt;QLabel&lt;/code&gt; displaying a &lt;code&gt;QPixmap&lt;/code&gt; that we draw onto.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPixmap, QPainter, QPen, QFont
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QPainter Coordinates")

        canvas = QPixmap(400, 300)
        canvas.fill(Qt.white)

        painter = QPainter(canvas)

        # Draw the line.
        pen = QPen(Qt.blue, 2)
        painter.setPen(pen)
        painter.drawLine(10, 10, 300, 200)

        # Annotate the start point.
        pen = QPen(Qt.red, 6)
        painter.setPen(pen)
        painter.drawPoint(10, 10)

        painter.setPen(QPen(Qt.black))
        painter.setFont(QFont("Arial", 10))
        painter.drawText(20, 15, "(10, 10)")

        # Annotate the end point.
        pen = QPen(Qt.red, 6)
        painter.setPen(pen)
        painter.drawPoint(300, 200)

        painter.setPen(QPen(Qt.black))
        painter.drawText(220, 220, "(300, 200)")

        painter.end()

        label = QLabel()
        label.setPixmap(canvas)
        self.setCentralWidget(label)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this and you'll see a blue line drawn from near the top-left corner of the canvas down to a point lower and to the right. The red dots and labels mark each endpoint, making it clear that &lt;code&gt;(10, 10)&lt;/code&gt; is near the top-left and &lt;code&gt;(300, 200)&lt;/code&gt; is toward the bottom-right.&lt;/p&gt;
&lt;p&gt;&lt;img alt="QPainter coordinates with annotated points" src="coordinates-annotated.png"/&gt;&lt;/p&gt;
&lt;p&gt;This is the expected behavior &amp;mdash; the y-axis points downward.&lt;/p&gt;
&lt;h2 id="why-does-it-work-this-way"&gt;Why does it work this way?&lt;/h2&gt;
&lt;p&gt;Screen coordinate systems with the origin at the top-left are a convention inherited from early computer displays, where the electron beam in a CRT monitor scanned from the top-left of the screen, line by line, downward. This convention carried forward into virtually all modern windowing and graphics systems, including Qt.&lt;/p&gt;
&lt;h2 id="converting-from-mathematical-coordinates"&gt;Converting from mathematical coordinates&lt;/h2&gt;
&lt;p&gt;If you're working with data that uses standard mathematical coordinates (origin at the bottom-left, y increasing upward), you'll need to convert the y values before drawing. The formula is straightforward:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;y_screen = height - 1 - y_math
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;y_screen&lt;/code&gt; is the y coordinate QPainter expects (origin at top-left).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;y_math&lt;/code&gt; is the y coordinate in standard math notation (origin at bottom-left).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;height&lt;/code&gt; is the height of your drawing surface in pixels.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;- 1&lt;/code&gt; is there because pixel coordinates are zero-indexed. A &lt;code&gt;QPixmap&lt;/code&gt; with a height of 300 has valid y coordinates from 0 to 299.&lt;/p&gt;
&lt;p&gt;Let's say you have a canvas that's 300 pixels tall, and you want to draw a line from the mathematical point &lt;code&gt;(10, 10)&lt;/code&gt; to &lt;code&gt;(300, 200)&lt;/code&gt; as if the origin were at the bottom-left. You'd convert each y coordinate:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;height = 300

# Mathematical coordinates.
x1, y1_math = 10, 10
x2, y2_math = 300, 200

# Convert y values for screen drawing.
y1_screen = height - 1 - y1_math  # 300 - 1 - 10 = 289
y2_screen = height - 1 - y2_math  # 300 - 1 - 200 = 99

painter.drawLine(x1, y1_screen, x2, y2_screen)
# Equivalent to: painter.drawLine(10, 289, 300, 99)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the line will go from near the &lt;em&gt;bottom&lt;/em&gt;-left upward to the right &amp;mdash; just like you'd expect on a math plot.&lt;/p&gt;
&lt;h2 id="a-helper-function-for-coordinate-conversion"&gt;A helper function for coordinate conversion&lt;/h2&gt;
&lt;p&gt;If you're doing a lot of drawing with mathematical coordinates, a small helper function keeps things tidy:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;def math_to_screen(x, y, height):
    """Convert mathematical (bottom-left origin) coordinates
    to screen (top-left origin) coordinates."""
    return x, height - 1 - y
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can then use it like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;x1, y1 = math_to_screen(10, 10, canvas_height)
x2, y2 = math_to_screen(300, 200, canvas_height)
painter.drawLine(x1, y1, x2, y2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="comparing-both-coordinate-systems-side-by-side"&gt;Comparing both coordinate systems side by side&lt;/h2&gt;
&lt;p&gt;This complete example draws the same line using both coordinate systems, so you can see the difference clearly. The left canvas uses QPainter's native coordinates (origin top-left), and the right canvas converts from mathematical coordinates (origin bottom-left).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPixmap, QPainter, QPen, QFont
from PyQt6.QtWidgets import (
    QApplication, QLabel, QMainWindow, QHBoxLayout, QVBoxLayout, QWidget,
)


def math_to_screen(x, y, height):
    """Convert mathematical (bottom-left origin) coordinates
    to screen (top-left origin) coordinates."""
    return x, height - 1 - y


def draw_annotated_line(canvas, x1, y1, x2, y2, label_start, label_end):
    """Draw a line on a QPixmap with annotated endpoints."""
    painter = QPainter(canvas)

    # Draw the line.
    pen = QPen(Qt.blue, 2)
    painter.setPen(pen)
    painter.drawLine(x1, y1, x2, y2)

    # Draw and label the start point.
    painter.setPen(QPen(Qt.red, 6))
    painter.drawPoint(x1, y1)
    painter.setPen(QPen(Qt.black))
    painter.setFont(QFont("Arial", 9))
    painter.drawText(x1 + 8, y1 + 5, label_start)

    # Draw and label the end point.
    painter.setPen(QPen(Qt.red, 6))
    painter.drawPoint(x2, y2)
    painter.setPen(QPen(Qt.black))
    painter.drawText(x2 - 80, y2 + 20, label_end)

    painter.end()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Coordinate System Comparison")

        canvas_width = 350
        canvas_height = 300

        # --- Left canvas: native QPainter coordinates ---
        canvas_native = QPixmap(canvas_width, canvas_height)
        canvas_native.fill(Qt.white)
        draw_annotated_line(
            canvas_native,
            10, 10, 300, 200,
            "(10, 10)", "(300, 200)",
        )

        label_native = QLabel()
        label_native.setPixmap(canvas_native)

        title_native = QLabel("Screen coordinates\n(origin top-left)")
        title_native.setAlignment(Qt.AlignCenter)
        title_native.setStyleSheet("font-weight: bold;")

        left_layout = QVBoxLayout()
        left_layout.addWidget(title_native)
        left_layout.addWidget(label_native)

        # --- Right canvas: mathematical coordinates converted ---
        canvas_math = QPixmap(canvas_width, canvas_height)
        canvas_math.fill(Qt.white)

        sx1, sy1 = math_to_screen(10, 10, canvas_height)
        sx2, sy2 = math_to_screen(300, 200, canvas_height)
        draw_annotated_line(
            canvas_math,
            sx1, sy1, sx2, sy2,
            f"math(10,10) &amp;rarr; screen({sx1},{sy1})",
            f"math(300,200) &amp;rarr; screen({sx2},{sy2})",
        )

        label_math = QLabel()
        label_math.setPixmap(canvas_math)

        title_math = QLabel("Math coordinates converted\n(origin bottom-left)")
        title_math.setAlignment(Qt.AlignCenter)
        title_math.setStyleSheet("font-weight: bold;")

        right_layout = QVBoxLayout()
        right_layout.addWidget(title_math)
        right_layout.addWidget(label_math)

        # --- Combine both sides ---
        main_layout = QHBoxLayout()
        main_layout.addLayout(left_layout)
        main_layout.addLayout(right_layout)

        container = QWidget()
        container.setLayout(main_layout)
        self.setCentralWidget(container)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When you run this, you'll see two canvases side by side. On the left, the line slopes downward from the top-left, which is what QPainter naturally produces. On the right, the same mathematical coordinates have been converted, so the line slopes upward from the bottom-left &amp;mdash; matching what you'd see on a standard math plot.&lt;/p&gt;
&lt;h2 id="drawing-axes-to-orient-yourself"&gt;Drawing axes to orient yourself&lt;/h2&gt;
&lt;p&gt;When you're experimenting with coordinates, it can help to draw a simple set of axes on your canvas. Here's a quick helper that draws x and y axes with the origin marked:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPixmap, QPainter, QPen, QFont
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow


def draw_axes(painter, width, height):
    """Draw simple x and y axes with labels."""
    painter.setPen(QPen(Qt.gray, 1, Qt.DashLine))

    # X-axis along the top (y=0).
    painter.drawLine(0, 0, width - 1, 0)

    # Y-axis along the left (x=0).
    painter.drawLine(0, 0, 0, height - 1)

    # Label the origin.
    painter.setPen(QPen(Qt.darkGray))
    painter.setFont(QFont("Arial", 8))
    painter.drawText(5, 15, "(0, 0)")

    # Label the x direction.
    painter.drawText(width - 60, 15, f"x &amp;rarr; ({width - 1})")

    # Label the y direction.
    painter.save()
    painter.translate(15, height - 10)
    painter.drawText(0, 0, f"y &amp;darr; ({height - 1})")
    painter.restore()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QPainter Axes")

        canvas_width = 400
        canvas_height = 300

        canvas = QPixmap(canvas_width, canvas_height)
        canvas.fill(Qt.white)

        painter = QPainter(canvas)
        draw_axes(painter, canvas_width, canvas_height)

        # Draw some points to see where they land.
        points = [
            (50, 50),
            (200, 150),
            (350, 250),
            (350, 50),
            (50, 250),
        ]

        painter.setPen(QPen(Qt.red, 6))
        for x, y in points:
            painter.drawPoint(x, y)

        painter.setPen(QPen(Qt.black))
        painter.setFont(QFont("Arial", 9))
        for x, y in points:
            painter.drawText(x + 6, y - 6, f"({x}, {y})")

        painter.end()

        label = QLabel()
        label.setPixmap(canvas)
        self.setCentralWidget(label)


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This draws the axes along the top and left edges of the canvas and plots several points with their coordinates labeled. It's a great way to build intuition about where things will appear.&lt;/p&gt;
&lt;h2 id="valid-coordinate-ranges"&gt;Valid coordinate ranges&lt;/h2&gt;
&lt;p&gt;One more thing to keep in mind: pixel coordinates on a &lt;code&gt;QPixmap&lt;/code&gt; are zero-indexed. If you create a pixmap with:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;canvas = QPixmap(400, 300)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then the valid coordinate ranges are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;x&lt;/strong&gt;: 0 to 399 (that's &lt;code&gt;width - 1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;y&lt;/strong&gt;: 0 to 299 (that's &lt;code&gt;height - 1&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Drawing outside these ranges won't cause an error, but anything beyond the edges simply won't be visible.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;The QPainter coordinate system places &lt;code&gt;(0, 0)&lt;/code&gt; at the top-left of the drawing surface, with x increasing to the right and y increasing downward. This is standard across virtually all screen-based graphics systems.&lt;/p&gt;
&lt;p&gt;If you need to work with mathematical coordinates where &lt;code&gt;(0, 0)&lt;/code&gt; is at the bottom-left and y increases upward, you can convert using the formula:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;y_screen = height - 1 - y_math
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once you've internalized this, drawing with QPainter becomes predictable. When in doubt, drop some annotated points on your canvas &amp;mdash; seeing the coordinates labeled right next to the dots is the fastest way to confirm everything is landing where you expect.&lt;/p&gt;
&lt;p&gt;For more details on Qt's coordinate system, take a look at the &lt;a href="https://doc.qt.io/qt-5/coordsys.html"&gt;official Qt coordinate system documentation&lt;/a&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt6"/><category term="pyqt"/><category term="python"/><category term="qpainter"/><category term="drawing"/><category term="coordinates"/><category term="qt"/><category term="qt6"/></entry></feed>