python / python/cpython

Turtle graphics window positioning glitch and rendering corruption on WSLg

オープン
#132,430 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

OS-windows stdlib type-bug
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

Bug report

Bug description:

I don't know whether to call this a bug, but I wanted to bring this issue to your attention here.
If this is not the appropriate place for this report, I would appreciate it if you could move it to the correct section/tracker.

Subject: Turtle graphics window positioning glitch and rendering corruption on WSLg (Windows Subsystem for Linux GUI)

Environment:

  • Operating System: Windows 11 with WSL2 enabled

  • WSL Distribution: Ubuntu 24.10

  • WSL GUI Support: WSLg

  • Python Version: 3.12.7, 3.13.0 (Standard library turtle module)

  • Key Environment Variables: DISPLAY=:0, WAYLAND_DISPLAY=wayland-0 (confirming WSLg's XWayland and Wayland setup)

Problem Description:

When using the standard turtle module within a WSLg environment, attempting to set the initial window position using turtle.Screen().setup(..., startx=X, starty=Y) leads to unexpected behavior and rendering issues.

  1. The turtle graphics window initially appears at a seemingly default or incorrect location (often the top-left corner).

  2. Immediately after appearing, the window attempts to move to the position specified by the startx and starty arguments in the setup() method.

  3. After this move, the window's contents become corrupted. Often, only a small portion (usually the top-left corner) is rendered, the rest might be black, frozen, or visually glitched. The window may become unresponsive.

This issue makes it impossible to reliably position the turtle window at a specific starting location using the standard setup() method in WSLg.

Expected Behavior:

The turtle window should appear directly at the coordinates specified by startx and starty in the screen.setup() call, without first appearing elsewhere, and the window content should render correctly.

Steps to Reproduce:

The following minimal code consistently reproduces the issue in a WSLg environment:

import turtle

# Define desired window properties
win_width = 600
win_height = 400
# Use coordinates clearly different from the default top-left (0,0)
start_pos_x = 250
start_pos_y = 150

print(f"Attempting to set window to {win_width}x{win_height} at ({start_pos_x}, {start_pos_y})")

try:
    screen = turtle.Screen()
    # Attempt to set size and initial position
    screen.setup(width=win_width, height=win_height, startx=start_pos_x, starty=start_pos_y)
    screen.title("WSLg Turtle Positioning Test")

    # Draw something simple to ensure the window content is visible
    pen = turtle.Turtle()
    pen.shape("turtle")
    pen.color("blue")
    pen.penup()
    pen.goto(0, 0)
    pen.pendown()
    pen.write(f"Target: ({start_pos_x}, {start_pos_y})", align="center", font=("Arial", 12, "normal"))
    pen.circle(100)
    pen.hideturtle()

    print("Window setup complete. Click screen to exit.")
    # Keep the window open until clicked
    screen.exitonclick()

except Exception as e:
    print(f"An error occurred: {e}")

print("Program finished.")

Observed Behavior (in WSLg):

  • The window first flashes briefly at or near the top-left corner (0,0).

  • It then moves to the approximate location (250, 150).

  • The content area of the window becomes corrupted (partially drawn, black, unresponsive).

Analysis / Suspected Cause:

The issue seems related to the timing and mechanism by which turtle.Screen applies the window geometry (position and size) after creating the underlying tkinter.Tk window. It appears turtle might be:

  1. Creating the Tk window.

  2. Making it visible (mapping it) implicitly or explicitly.

  3. Then, applying the geometry change to move it.

This sequence seems to cause issues with the WSLg display server (which involves XWayland translating X11 calls to the Wayland compositor, finally rendered on the Windows desktop). The rapid create-show-move sequence likely leads to a race condition or improper handling of the graphics context by the intermediate layers in WSLg.

This hypothesis is supported by the fact that using tkinter directly within the same WSLg environment to set the window geometry before the main loop starts works correctly without glitches.

Successful Workaround:

The issue can be reliably circumvented by avoiding turtle.Screen() and instead using turtle.RawTurtle on a manually created tkinter.Canvas:

  1. Create a tkinter.Tk() root window.

  2. Set the desired geometry (size and position) directly on the root window using root.geometry(f"{width}x{height}+{startx}+{starty}"). This step works correctly in WSLg.

  3. Create a tkinter.Canvas widget inside the root window.

  4. Instantiate turtle.RawTurtle(canvas) targeting the created canvas.

  5. Perform all drawing operations using the RawTurtle instance.

  6. Start the tkinter event loop using root.mainloop().

This approach bypasses the problematic window initialization/positioning logic within turtle.Screen.setup() and leverages tkinter's direct geometry management, which appears compatible with WSLg.

(Optional: Reference to helper function)
A helper function encapsulating this workaround was developed:

import tkinter as tk
import turtle

def setup_turtle_canvas(width=600, height=400, title="Turtle Graphics", center=True):
    """
    Set RawTurtle on Tkinter Canvas
    
    Args:
        width (int): window width
        height (int): window height
        title (str): windot title
        center (bool): center position if True

    Returns:
        tuple: (turtle.RawTurtle, tkinter.Tk)
               - RawTurtle object
               - root window object for mainloop()
    """
    root = tk.Tk()
    root.title(title)

    start_x = 0
    start_y = 0
    if center:
        try:
            # preventing window flickering
            root.withdraw()
            root.update_idletasks()
            screen_width = root.winfo_screenwidth()
            screen_height = root.winfo_screenheight()
            start_x = max(0, int((screen_width / 2) - (width / 2)))
            start_y = max(0, int((screen_height / 2) - (height / 2)))
            root.deiconify() # display again
        except Exception as e:
            print(f"Warning: Could not center window ({e}). Placing at top-left.")
            if root.state() == 'withdrawn': # display again if withdrawn
                 root.deiconify()


    # set geometry
    root.geometry(f"{width}x{height}+{start_x}+{start_y}")

    # create canvas and positioning 
    canvas = tk.Canvas(root, width=width, height=height)
    canvas.pack(fill=tk.BOTH, expand=True)

    # create RawTurtle
    pen = turtle.RawTurtle(canvas)

    # --- for exitonclick function ---
    def _on_canvas_click(event):
        """internal function when canvas clicked"""
        print("Canvas clicked - exiting.")
        # root.destroy()
        root.quit()      # quit mainloop

    canvas.bind("<Button-1>", _on_canvas_click)
    # --- end of exitonclick function ---

    return pen, root

Suggestion for Potential Fix:

Could the turtle.py module (specifically the _Screen class's init or setup methods) be adjusted to ensure the tkinter root window's geometry is set before the window is mapped or becomes fully visible to the window manager? Perhaps ensuring root.update_idletasks() is called after setting geometry but before drawing commences might help synchronize the state with the window manager in complex environments like WSLg. However, care must be taken to avoid introducing side effects in other standard environments.

We hope this report is helpful for improving turtle's compatibility with modern cross-platform GUI environments like WSLg, although it seems like older modules such as turtle haven't received much attention recently... .

CPython versions tested on:

3.13

Operating systems tested on:

Windows

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

turtle.py の Screen/setup パスから始め、WSLg 上の Ubuntu で Python 3.12 または 3.13 を使って最小限の例を再現します。Screen の動作を、報告で説明されている tkinter のジオメトリおよび RawTurtle の回避策と比較します。ウィンドウが要求された位置に開き、その内容が破損せずに描画されれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
desktop
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
38/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。