Last active
October 29, 2025 12:16
-
-
Save Wowfunhappy/48df1d390f0eb8e25088d3df4a904684 to your computer and use it in GitHub Desktop.
Setup Thonny on OS X 10.9. Run from a Python 3.10 virtual environment created with --system-site-packages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Complete Thonny setup script for Tk 8.5 compatibility | |
| - Installs/reinstalls Thonny | |
| - Converts PNG images to GIF with transparency | |
| - Patches code for compatibility and updates all PNG references to GIF | |
| (Script written by Claude 3.5 Sonnet) | |
| """ | |
| import sys | |
| import subprocess | |
| from pathlib import Path | |
| from PIL import Image | |
| import re | |
| def main(): | |
| print("=" * 60) | |
| print("Thonny Tk 8.5 Compatibility Setup") | |
| print("=" * 60) | |
| # Step 1: Install Thonny | |
| print("\n[1/4] Installing Thonny...") | |
| subprocess.run([ | |
| sys.executable, "-m", "pip", "install", "--force-reinstall", "thonny" | |
| ], check=True) | |
| print("✓ Thonny installed") | |
| # Step 2: Convert images | |
| print("\n[2/4] Converting PNG images to GIF with transparency...") | |
| res_dir = Path("lib/python3.10/site-packages/thonny/res") | |
| png_files = list(res_dir.rglob("*.png")) | |
| print(f"Found {len(png_files)} PNG files to convert") | |
| converted = 0 | |
| errors = 0 | |
| for png_path in png_files: | |
| gif_path = png_path.with_suffix('.gif') | |
| try: | |
| with Image.open(png_path) as img: | |
| if img.mode == 'RGBA': | |
| # Split the image | |
| alpha = img.split()[3] | |
| # Create RGB version | |
| rgb_img = Image.new("RGB", img.size, (255, 255, 255)) | |
| rgb_img.paste(img, mask=alpha) | |
| # Convert to palette mode with only 254 colors | |
| palette_img = rgb_img.quantize(colors=254, method=2) | |
| # Get palette and alpha data | |
| palette_pixels = palette_img.load() | |
| alpha_pixels = alpha.load() | |
| # Set transparent pixels to index 255 | |
| for y in range(img.height): | |
| for x in range(img.width): | |
| if alpha_pixels[x, y] < 20: | |
| palette_pixels[x, y] = 255 | |
| # Save with transparency at index 255 | |
| palette_img.save(gif_path, 'GIF', transparency=255, optimize=False) | |
| else: | |
| # No alpha channel, just convert | |
| if img.mode not in ('RGB', 'P'): | |
| img = img.convert('RGB') | |
| img.save(gif_path, 'GIF') | |
| converted += 1 | |
| if converted % 20 == 0: | |
| print(f" Converted {converted}/{len(png_files)}...") | |
| except Exception as e: | |
| print(f" Error converting {png_path}: {e}") | |
| errors += 1 | |
| print(f"✓ Converted {converted} files ({errors} errors)") | |
| # Remove PNG files | |
| print(" Removing PNG files...") | |
| for png_path in png_files: | |
| png_path.unlink() | |
| print("✓ PNG files removed") | |
| # Step 3: Patch all Python files to use .gif instead of .png | |
| print("\n[3/4] Patching all .png references to .gif...") | |
| thonny_dir = Path("lib/python3.10/site-packages/thonny") | |
| py_files = list(thonny_dir.rglob("*.py")) | |
| patched_files = 0 | |
| for py_file in py_files: | |
| try: | |
| content = py_file.read_text() | |
| original_content = content | |
| # Replace all .png references with .gif | |
| # Handle various patterns of PNG references | |
| patterns = [ | |
| (r'\.png"', '.gif"'), | |
| (r"\.png'", ".gif'"), | |
| (r'\.png\)', '.gif)'), | |
| (r'\.png,', '.gif,'), | |
| (r'\.png\s', '.gif '), | |
| (r'\.png$', '.gif'), | |
| ] | |
| for pattern, replacement in patterns: | |
| content = re.sub(pattern, replacement, content, flags=re.MULTILINE) | |
| if content != original_content: | |
| py_file.write_text(content) | |
| patched_files += 1 | |
| print(f" Patched {py_file.name}") | |
| except Exception as e: | |
| print(f" Error patching {py_file}: {e}") | |
| print(f"✓ Patched {patched_files} files with PNG references") | |
| # Step 4: Apply specific patches | |
| print("\n[4/4] Applying specific compatibility patches...") | |
| # Patch 1: Fix crimson color in codeview.py | |
| codeview_path = Path("lib/python3.10/site-packages/thonny/codeview.py") | |
| if codeview_path.exists(): | |
| content = codeview_path.read_text() | |
| content = content.replace( | |
| 'self._gutter.tag_configure("breakpoint", foreground="crimson")', | |
| 'self._gutter.tag_configure("breakpoint", foreground="#DC143C")' | |
| ) | |
| codeview_path.write_text(content) | |
| print("✓ Patched codeview.py") | |
| # Patch 2: Fix crimson color in base_syntax_themes.py | |
| themes_path = Path("lib/python3.10/site-packages/thonny/plugins/base_syntax_themes.py") | |
| if themes_path.exists(): | |
| content = themes_path.read_text() | |
| content = content.replace( | |
| '"breakpoint": {"foreground": "crimson"}', | |
| '"breakpoint": {"foreground": "#DC143C"}' | |
| ) | |
| themes_path.write_text(content) | |
| print("✓ Patched base_syntax_themes.py") | |
| # Patch 3: Add error handling to workbench.py | |
| workbench_path = Path("lib/python3.10/site-packages/thonny/workbench.py") | |
| if workbench_path.exists(): | |
| content = workbench_path.read_text() | |
| # First replacement - the else block | |
| old_text1 = """ else: | |
| img = tk.PhotoImage(file=filename) | |
| # can't use zoom method, because this doesn't allow name | |
| img2 = tk.PhotoImage(tk_name) | |
| self.tk.call( | |
| img2, | |
| "copy", | |
| img.name, | |
| "-zoom", | |
| 2, | |
| 2, | |
| ) | |
| self._images.add(img2) | |
| return img2 | |
| img = tk.PhotoImage(tk_name, file=filename) | |
| self._images.add(img) | |
| return img""" | |
| new_text1 = """ else: | |
| try: | |
| img = tk.PhotoImage(file=filename) | |
| # can't use zoom method, because this doesn't allow name | |
| img2 = tk.PhotoImage(tk_name) | |
| self.tk.call( | |
| img2, | |
| "copy", | |
| img.name, | |
| "-zoom", | |
| 2, | |
| 2, | |
| ) | |
| self._images.add(img2) | |
| return img2 | |
| except Exception: | |
| return None | |
| try: | |
| img = tk.PhotoImage(tk_name, file=filename) | |
| self._images.add(img) | |
| return img | |
| except Exception: | |
| return None""" | |
| if old_text1 in content: | |
| content = content.replace(old_text1, new_text1) | |
| workbench_path.write_text(content) | |
| print("✓ Patched workbench.py") | |
| else: | |
| print("⚠ workbench.py already patched or format changed") | |
| print("\n" + "=" * 60) | |
| print("Setup complete! You can now run: thonny &") | |
| print("=" * 60) | |
| if __name__ == "__main__": | |
| main()(Script written by Claude 3.5 Sonnet) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment