diff --git a/.editorconfig b/.editorconfig
index d0d5825..68cec78 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,4 +12,4 @@ indent_size = 4
indent_size = 2
[*.{md,rst}]
-trim_trailing_whitespace = false
\ No newline at end of file
+trim_trailing_whitespace = false
diff --git a/.flake8 b/.flake8
index e0ea542..8dd399a 100644
--- a/.flake8
+++ b/.flake8
@@ -1,3 +1,3 @@
[flake8]
max-line-length = 88
-extend-ignore = E203
\ No newline at end of file
+extend-ignore = E203
diff --git a/.gitattribute b/.gitattribute
index 71027c2..478b946 100644
--- a/.gitattribute
+++ b/.gitattribute
@@ -18,4 +18,4 @@ LICENSE text eol=lf
.gitignore text eol=lf
.gitattribute text eol=lf
.flake8 text eol=lf
-.pylintrc text eol=lf
\ No newline at end of file
+.pylintrc text eol=lf
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..680f85b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,33 @@
+version: 2
+updates:
+ # Enable version updates for Python dependencies
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ day: "monday"
+ time: "04:00"
+ open-pull-requests-limit: 10
+ reviewers:
+ - "PermutaTriangle"
+ assignees:
+ - "PermutaTriangle"
+ commit-message:
+ prefix: "deps"
+ include: "scope"
+
+ # Enable version updates for GitHub Actions
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ day: "monday"
+ time: "04:00"
+ open-pull-requests-limit: 5
+ reviewers:
+ - "PermutaTriangle"
+ assignees:
+ - "PermutaTriangle"
+ commit-message:
+ prefix: "ci"
+ include: "scope"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..e476994
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,40 @@
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ master, develop ]
+ pull_request:
+ branches: [ master ]
+ schedule:
+ - cron: '30 2 * * 1' # Weekly on Mondays at 02:30 UTC
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'python' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 691a48b..c93e1eb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,22 +9,21 @@ jobs:
fail-fast: false
matrix:
include:
- - python: 3.8
+ # Linting and type checking (run on Python 3.11)
+ - python: "3.11"
toxenv: flake8
os: ubuntu-latest
- - python: 3.8
+ - python: "3.11"
toxenv: mypy
os: ubuntu-latest
- - python: 3.8
+ - python: "3.11"
toxenv: pylint
os: ubuntu-latest
- - python: 3.8
+ - python: "3.11"
toxenv: black
os: ubuntu-latest
- - python: 3.7
- toxenv: py37
- os: ubuntu-latest
+ # Python version testing (Linux)
- python: 3.8
toxenv: py38
os: ubuntu-latest
@@ -34,28 +33,47 @@ jobs:
- python: "3.10"
toxenv: py310
os: ubuntu-latest
- - python: pypy-3.7
- toxenv: pypy37
+ - python: "3.11"
+ toxenv: py311
+ os: ubuntu-latest
+ - python: "3.12"
+ toxenv: py312
+ os: ubuntu-latest
+ - python: "3.13"
+ toxenv: py313
+ os: ubuntu-latest
+ - python: pypy-3.8
+ toxenv: pypy38
+ os: ubuntu-latest
+ - python: pypy-3.9
+ toxenv: pypy39
+ os: ubuntu-latest
+ - python: pypy-3.10
+ toxenv: pypy310
os: ubuntu-latest
- - python: 3.8
- toxenv: py38
+ # Cross-platform testing (Python 3.11)
+ - python: "3.11"
+ toxenv: py311
os: macos-latest
- - python: 3.8
- toxenv: py38
+ - python: "3.11"
+ toxenv: py311
os: windows-latest
runs-on: ${{ matrix.os }}
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-python@v2
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
with:
- python-version: ${{ matrix.python }}
+ python-version: ${{ matrix.python }}
+ allow-prereleases: true
- name: install dependencies
- run: python -m pip install --upgrade pip tox
+ run: |
+ python -m pip install --upgrade pip setuptools
+ python -m pip install tox
- name: run
env:
TOXENV: ${{ matrix.toxenv }}
run: tox
- name: setup
- run: python setup.py install
\ No newline at end of file
+ run: python setup.py install
diff --git a/.gitignore b/.gitignore
index 20229bc..4eac1e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,8 +86,8 @@ target/
profile_default/
ipython_config.py
-# pyenv
-.python-version
+# pyenv (but we want to track our .python-version file)
+# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
@@ -142,4 +142,63 @@ dmypy.json
.vscode/
# Program's output
-tilingsgui/exports/
\ No newline at end of file
+tilingsgui/exports/
+exports/
+
+# macOS
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# Windows
+*.stackdump
+
+# Linux
+*~
+
+# Temporary/backup files
+*.tmp
+*.temp
+*.bak
+*.swp
+*.swo
+*~
+
+# IDE specific files (additional)
+.idea/
+*.iml
+*.ipr
+*.iws
+.vscode/settings.json
+.vscode/launch.json
+.vscode/tasks.json
+
+# Pre-commit
+.pre-commit-config.yaml.bak
+
+# Ruff cache (modern Python linter)
+.ruff_cache/
+
+# Modern type checkers
+.pyright/
+
+# Coverage.py
+.coverage.*
+coverage.xml
+htmlcov/
+
+# Duplicate files (with version numbers or " 2" suffix)
+*" 2".*
+* 2.*
+
+# Temporary export files
+*.json.tmp
+tilings_export.json
+
+# Development files that shouldn't be committed
+hamstur.py
+2025-MODERNIZATION-PLAN.md
diff --git a/.isort.cfg b/.isort.cfg
index 3884dca..92c8e2d 100644
--- a/.isort.cfg
+++ b/.isort.cfg
@@ -5,4 +5,4 @@ multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
-line_length=88
\ No newline at end of file
+line_length=88
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ee9075b..1df3071 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,5 +1,21 @@
repos:
- repo: https://github.com/psf/black
- rev: 22.10.0
+ rev: 24.8.0
hooks:
- - id: black
\ No newline at end of file
+ - id: black
+ language_version: python3.11
+
+ - repo: https://github.com/pycqa/flake8
+ rev: 7.1.1
+ hooks:
+ - id: flake8
+ additional_dependencies: [flake8-isort]
+
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.6.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: check-yaml
+ - id: check-added-large-files
+ - id: check-merge-conflict
diff --git a/.pylintrc b/.pylintrc
index e1de93b..2f899e8 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,8 +1,8 @@
[MASTER]
ignore-patterns=test_.*?py
-init-hook="from pylint.config import find_pylintrc; import os, sys; sys.path.append(os.path.dirname(find_pylintrc())+'/tilingsgui')"
+init-hook="import os, sys; sys.path.append(os.path.join(os.path.dirname(__file__), 'tilingsgui'))"
max-args=10
max-module-lines=1200
-disable=too-few-public-methods,too-many-lines
-good-names=r,c,x,y,h,w,i,j,k,n,x1,x2,y1,y2,g,b,dx,dy
\ No newline at end of file
+disable=too-few-public-methods,too-many-lines,too-many-positional-arguments
+good-names=r,c,x,y,h,w,i,j,k,n,x1,x2,y1,y2,g,b,dx,dy
diff --git a/.python-version b/.python-version
new file mode 100644
index 0000000..2c07333
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.11
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6210168..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-language: python
-matrix:
- include:
- - python: 3.8
- env: TOXENV=flake8
-install:
-- pip install tox
-script:
-- tox
-deploy:
- skip_cleanup: true
- provider: pypi
- user: __token__
- password:
- secure: ZEsM76obFP9ZIGdHkKIgMFc/7qN33ZCO/2Tl0E8noUOHuL/dPkKeG1VWD7XiXupljULgRkxKInrz5aw/423+qwy1kQw2rDxdxbs4ITAbp3xEd4X7N2WwvYp7biuw5z4I2lzY7TI6HvHxFeN25Wvo1qIc7SIMMcYmSAY7pOs7qW+ljfrg9PhBJZBk0zZ3ISAu8npkIVa9FKaYTbx0lq9GWdy3bovkGER4DwcjGB6Cx0OCSrLxwYqNi34xlSy7hBQugQp85SqSlwOpgxLGgxr0/xyW73iQdoQhcFdl3Aoupudu/suy0w7JShANGeD2cWtXs5a028w79tKwKmlb11gSFbLT9fdhxdSPU87qm5EOye0loCg5pGQtZvZ2Zf+WyIFaPWl7UewcBDHNSx2APUHokpI9N6qoaI94CcY5UHCmlXGc2PMN00WmhTlHGYsFEADoFa5brqBNEvP5XpHa8eT/w63tGG/v/a9NdfLMzktYemUIGdNZrOj8V1YGjd28HsxE2/zF/kG9Qbq1878tYmocpxro8gQXdbzc1DyiXZsyDWPRSv7WCIXl07Y2nWIqKuuQmmrFlxTw+SmRiIXpzl2YZ2c5UqPJFNcaJ5fZ2U+MwadbkhuANIYn5vc4AkZyz2L/jeyKz7wNB7Ar8DgAKxKkZ0gJv7AICZTGEXXEtIeMsdk=
- skip_existing: true
- on:
- branch: master
- condition: "$TRAVIS_OS_NAME = linux"
diff --git a/README.rst b/README.rst
index ac5c932..e33a6d6 100644
--- a/README.rst
+++ b/README.rst
@@ -21,11 +21,24 @@ Pyperclip requires clipboard tools that might not come pre-installed.
sudo apt-get install xclip
-Without them the app still works but pasting won’t.
+Without them the app still works but pasting won't.
+
+Note for macOS
+~~~~~~~~~~~~~~
+
+PyObjC is automatically installed on macOS systems for proper Cocoa/OpenGL integration with pyglet 2.0+. If you encounter OpenGL surface errors, ensure PyObjC is installed:
+
+.. code:: sh
+
+ pip install pyobjc-core pyobjc-framework-Cocoa
+
+This is handled automatically during normal installation.
+
+**PyPy Users**: PyObjC doesn't support PyPy. TilingsGUI automatically configures pyglet to use shadow context disabled mode for PyPy compatibility on macOS.
Known issues
------------
-*
+*
Report a bug
~~~~~~~~~~~~
@@ -79,9 +92,9 @@ The following two inputs are two ways of producing the same initial tiling.
.. code::
1432_12345
-
+
{"class_module": "tilings.tiling", "comb_class": "Tiling", "obstructions": [{"patt": [0, 3, 2, 1], "pos": [[0, 0], [0, 0], [0, 0], [0, 0]]}, {"patt": [0, 1, 2, 3, 4], "pos": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}], "requirements": [], "assumptions": []}
-
+
The initial tiling in question would be the following.
.. code:: sh
@@ -90,7 +103,7 @@ The initial tiling in question would be the following.
|1|
+-+
1: Av(0321, 01234)
-
+
Cell insertion
~~~~~~~~~~~~~~
@@ -131,7 +144,7 @@ By clicking a point of a requirement, we pass its gridded permutation along with
Fusion
~~~~~~
-Let ``c_r`` and ``c_c`` be the row and column respectively of the clicked cell. There are 4 types of fusions available. Fusion with ``row=c_r``, |fusion_r|, fusion with ``col=c_c``, |fusion_c|, component fusion with ``row=c_r``, |fusion_comp_r|, and component fusion with ``col=c_c``, |fusion_comp_c|. If the fusion are invalid, then exceptions are caught and nothing happens.
+Let ``c_r`` and ``c_c`` be the row and column respectively of the clicked cell. There are 4 types of fusions available. Fusion with ``row=c_r``, |fusion_r|, fusion with ``col=c_c``, |fusion_c|, component fusion with ``row=c_r``, |fusion_comp_r|, and component fusion with ``col=c_c``, |fusion_comp_c|. If the fusion are invalid, then exceptions are caught and nothing happens.
Fusion:
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..e5b7775
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,47 @@
+# Security Policy
+
+## Supported Versions
+
+We actively support the following versions of TilingsGUI with security updates:
+
+| Version | Supported |
+| ------- | ------------------ |
+| 0.2.x | :white_check_mark: |
+| < 0.2 | :x: |
+
+## Reporting a Vulnerability
+
+If you discover a security vulnerability in TilingsGUI, please report it to us privately. We take security seriously and will respond promptly to legitimate security concerns.
+
+### How to Report
+
+1. **Email**: Send an email to permutatriangle@gmail.com with the subject line "Security Vulnerability in TilingsGUI"
+2. **Include**:
+ - A detailed description of the vulnerability
+ - Steps to reproduce the issue
+ - Potential impact assessment
+ - Any suggested fixes (if available)
+
+### What to Expect
+
+- **Acknowledgment**: We will acknowledge receipt of your report within 48 hours
+- **Initial Assessment**: We will provide an initial assessment within 5 business days
+- **Resolution Timeline**: We aim to resolve critical security issues within 30 days
+- **Disclosure**: We will coordinate with you on appropriate disclosure timing
+
+### Security Best Practices
+
+When using TilingsGUI:
+- Keep your installation up to date with the latest version
+- Only load tilings from trusted sources
+- Be cautious when running TilingsGUI with elevated privileges
+- Report any suspicious behavior or unexpected security prompts
+
+## Scope
+
+This security policy covers:
+- The TilingsGUI application itself
+- Dependencies and third-party libraries
+- Build and deployment processes
+
+Thank you for helping keep TilingsGUI secure!
diff --git a/pyproject.toml b/pyproject.toml
index f2d5bca..25e376c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,5 +1,9 @@
+[build-system]
+requires = ["setuptools>=45", "wheel"]
+build-backend = "setuptools.build_meta"
+
[tool.black]
-target-version = ['py37']
+target-version = ['py38', 'py39', 'py310', 'py311', 'py312', 'py313']
include = '\.pyi?$'
exclude = '''
(
@@ -18,4 +22,4 @@ exclude = '''
| foo.py # also separately exclude a file named foo.py in
# the root of the project
)
-'''
\ No newline at end of file
+'''
diff --git a/setup.py b/setup.py
index 522586d..4d06e32 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,11 @@ def get_version():
raise ValueError("Version not found in tilingsgui/__init__.py")
+def get_install_requires():
+ """Get install requirements."""
+ return ["pyperclip>=1.9.0", "pyglet>=2.0.0", "tilings>=2.5.0"]
+
+
setup(
name="tilingsgui",
version=get_version(),
@@ -31,8 +36,8 @@ def get_version():
},
packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
long_description=read("README.rst"),
- install_requires=["pyperclip==1.8.1", "pyglet==1.5.15", "tilings==2.5.0"],
- python_requires=">=3.6",
+ install_requires=get_install_requires(),
+ python_requires=">=3.8",
include_package_data=True,
classifiers=[
"Topic :: Education",
@@ -41,10 +46,12 @@ def get_version():
"Intended Audience :: Science/Research",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
diff --git a/tilingsgui/__init__.py b/tilingsgui/__init__.py
index af7642d..bb16f6e 100644
--- a/tilingsgui/__init__.py
+++ b/tilingsgui/__init__.py
@@ -1,4 +1,3 @@
-"""As the tilingsgui is not intened to be imported, this only holds the version.
-"""
+"""As the tilingsgui is not intened to be imported, this only holds the version."""
__version__ = "0.2.3"
diff --git a/tilingsgui/app.py b/tilingsgui/app.py
index d425915..2702dd1 100644
--- a/tilingsgui/app.py
+++ b/tilingsgui/app.py
@@ -5,10 +5,49 @@
# pylint: disable=abstract-method
-from typing import ClassVar, Tuple
+import sys
+from typing import ClassVar, Literal, Tuple
import pyglet
+# PyPy compatibility on macOS
+#
+# PyPy is not supported on macOS due to fundamental incompatibilities between PyPy's
+# ctypes implementation and macOS GUI frameworks. This issue affects ALL major Python
+# GUI libraries, not just pyglet.
+#
+# Investigation conducted (Oct 2025) found the following:
+#
+# 1. pyglet 2.0:
+# - Fails with IndexError/AttributeError in PyPy's ctypes when interfacing with
+# Cocoa/Objective-C bridge
+# - pyglet.options like shadow_window=False and osx_alt_loop=True do not help
+#
+# 2. Tkinter:
+# - Imports successfully but hangs when creating windows
+# - PyPy's bundled Tk requires manual mainloop() calls, breaking event-driven design
+# - Tested with PyPy 7.3.20 (Python 3.11.13) - same issues
+#
+# 3. PySide6 (Qt):
+# - Officially supports PyPy 3.8+ but no pre-built PyPy wheels available for macOS
+# - Would require building from source
+#
+# 4. wxPython:
+# - Community reports indicate it does not work with PyPy
+#
+# Root cause: PyPy's ctypes has subtle differences from CPython in callback and object
+# reference handling, which breaks all macOS GUI frameworks that use ctypes to interface
+# with Cocoa/Objective-C.
+#
+# Note: PyPy may work on Linux (X11) or Windows (Win32), as the issue is specific to
+# macOS's Cocoa backend.
+if sys.platform == "darwin" and sys.implementation.name == "pypy":
+ print("Error: PyPy is not supported on macOS.")
+ print("Reason: PyPy's ctypes is incompatible with macOS GUI frameworks (Cocoa).")
+ print("Please use CPython on macOS, or try PyPy on Linux/Windows.")
+ sys.exit(1)
+
+# pylint: disable=wrong-import-position
from .files import History, PathManager
from .graphics import Color
from .menu import RightMenu, TopMenu
@@ -22,13 +61,13 @@ class TilingGui(pyglet.window.Window):
_TITLE: ClassVar[str] = "Tilings GUI"
_MIN_WIDTH: ClassVar[int] = 500
_MIN_HEIGHT: ClassVar[int] = 400
- _INITIAL_WIDTH: ClassVar[int] = 800
- _INITIAL_HEIGHT: ClassVar[int] = 650
- _RIGHT_BAR_WIDTH: ClassVar[int] = 200
- _TOP_BAR_HEIGHT: ClassVar[int] = 24
- _CLEAR_COLOR: ClassVar[
- Tuple[float, float, float, float]
- ] = Color.alpha_extend_and_scale_to_01(Color.WHITE)
+ _INITIAL_WIDTH: ClassVar[int] = 1600
+ _INITIAL_HEIGHT: ClassVar[int] = 1200
+ _RIGHT_BAR_WIDTH: ClassVar[int] = 400
+ _TOP_BAR_HEIGHT: ClassVar[int] = 50
+ _CLEAR_COLOR: ClassVar[Tuple[float, float, float, float]] = (
+ Color.alpha_extend_and_scale_to_01(Color.WHITE)
+ )
def __init__(self, init_tiling: str, *args, **kargs) -> None:
"""Instantiate the parent window class and create all
@@ -90,7 +129,7 @@ def _initial_config(self) -> None:
"""Configuration done before starting."""
# Center the window within the os.
- screen = pyglet.canvas.Display().get_default_screen()
+ screen = pyglet.display.Display().get_default_screen() # type: ignore
self.set_location(
(screen.width - self.width) // 2, (screen.height - self.height) // 2
)
@@ -100,9 +139,9 @@ def _initial_config(self) -> None:
# Handle clearing the canvas on each draw.
pyglet.gl.glClearColor(*TilingGui._CLEAR_COLOR)
- self.push_handlers(on_draw=self.clear)
+ self.push_handlers(on_draw=self.clear) # pylint: disable=unreachable
- def on_resize(self, width: int, height: int) -> bool:
+ def on_resize(self, width: int, height: int) -> Literal[True]:
"""Event handler for the window resize event.
Args:
diff --git a/tilingsgui/events.py b/tilingsgui/events.py
index fcb3aec..b70ade1 100644
--- a/tilingsgui/events.py
+++ b/tilingsgui/events.py
@@ -1,5 +1,4 @@
-"""Event related module.
-"""
+"""Event related module."""
from typing import Iterable
diff --git a/tilingsgui/files.py b/tilingsgui/files.py
index 5f73c4d..dd5590b 100644
--- a/tilingsgui/files.py
+++ b/tilingsgui/files.py
@@ -1,5 +1,4 @@
-"""A collection of file and path related functionality.
-"""
+"""A collection of file and path related functionality."""
import json
import pathlib
diff --git a/tilingsgui/geometry.py b/tilingsgui/geometry.py
index af33417..4c013ec 100644
--- a/tilingsgui/geometry.py
+++ b/tilingsgui/geometry.py
@@ -1,5 +1,4 @@
-"""Mathematical geometric objects.
-"""
+"""Mathematical geometric objects."""
from typing import Iterator
diff --git a/tilingsgui/graphics.py b/tilingsgui/graphics.py
index 499b501..c1d23a5 100644
--- a/tilingsgui/graphics.py
+++ b/tilingsgui/graphics.py
@@ -1,11 +1,9 @@
-"""Drawable objects
-"""
+"""Drawable objects"""
-
-from math import cos, pi, sin
from typing import ClassVar, List, Tuple
import pyglet
+import pyglet.shapes
from .geometry import Point
@@ -18,9 +16,6 @@
class GeoDrawer:
"""A static class container of drawing methods."""
- _VERTEX_MODE: ClassVar[str] = "v2f"
- _COLOR_MODE: ClassVar[str] = "c3B"
-
@staticmethod
def draw_line_segment(
x1: float, y1: float, x2: float, y2: float, color: C3F
@@ -34,12 +29,9 @@ def draw_line_segment(
y2 (float): End y coordinate.
color (Tuple[float, float, float]): RGB valued color.
"""
- pyglet.graphics.draw(
- 2,
- pyglet.gl.GL_LINE_STRIP,
- (GeoDrawer._VERTEX_MODE, [x1, y1, x2, y2]),
- (GeoDrawer._COLOR_MODE, color * 2),
- )
+ color_255 = Color.scale_to_255(color)
+ line = pyglet.shapes.Line(x1, y1, x2, y2, color=color_255)
+ line.draw()
@staticmethod
def draw_circle(x: float, y: float, r: float, color: C3F, splits: int = 30) -> None:
@@ -53,17 +45,9 @@ def draw_circle(x: float, y: float, r: float, color: C3F, splits: int = 30) -> N
splits (int, optional): How detailed the polygon emulating a circle should
be. Higher values increase detail.
"""
- vertices = [x, y]
- for i in range(splits + 1):
- ang = 2 * pi * i / splits
- vertices.append(x + cos(ang) * r)
- vertices.append(y + sin(ang) * r)
- pyglet.graphics.draw(
- splits + 2,
- pyglet.gl.GL_TRIANGLE_FAN,
- (GeoDrawer._VERTEX_MODE, vertices),
- (GeoDrawer._COLOR_MODE, color * (splits + 2)),
- )
+ color_255 = Color.scale_to_255(color)
+ circle = pyglet.shapes.Circle(x, y, r, color=color_255, segments=splits)
+ circle.draw()
@staticmethod
def draw_point(point: Point, size: float, color: C3F) -> None:
@@ -87,12 +71,9 @@ def draw_rectangle(x: float, y: float, w: float, h: float, color: C3F) -> None:
h (float): Vertical length.
color (Tuple[float, float, float]): Fill color.
"""
- pyglet.graphics.draw(
- 4,
- pyglet.gl.GL_TRIANGLE_STRIP,
- (GeoDrawer._VERTEX_MODE, [x, y, x, y + h, x + w, y, x + w, y + h]),
- (GeoDrawer._COLOR_MODE, color * 4),
- )
+ color_255 = Color.scale_to_255(color)
+ rectangle = pyglet.shapes.Rectangle(x, y, w, h, color=color_255)
+ rectangle.draw()
@staticmethod
def draw_point_path(pnt_path: List[Point], color: C3F, point_size: float) -> None:
@@ -106,13 +87,13 @@ def draw_point_path(pnt_path: List[Point], color: C3F, point_size: float) -> Non
n = len(pnt_path)
if n > 0:
if n > 1:
- vertices = [coord for pnt in pnt_path for coord in pnt.coords()]
- pyglet.graphics.draw(
- n,
- pyglet.gl.GL_LINE_STRIP,
- (GeoDrawer._VERTEX_MODE, vertices),
- (GeoDrawer._COLOR_MODE, color * n),
- )
+ # Draw line segments between adjacent points
+ color_255 = Color.scale_to_255(color)
+ for i in range(n - 1):
+ p1, p2 = pnt_path[i], pnt_path[i + 1]
+ line = pyglet.shapes.Line(p1.x, p1.y, p2.x, p2.y, color=color_255)
+ line.draw()
+ # Draw points
for pnt in pnt_path:
GeoDrawer.draw_point(pnt, point_size, color)
@@ -264,13 +245,13 @@ class Color:
@staticmethod
def scale_to_01(color: C3I) -> C3F:
- """Scale color values from 0-255 to 0-1. Color can include alpha value.
+ """Scale color values from 0-255 to 0-1.
Args:
- color (Tuple[int, int, int]): A tuple of length 3.
+ color (Tuple[int, int, int]): RGB color in 0-255 range.
Returns:
- Tuple[float, float, float]: A scaled tuple.
+ Tuple[float, float, float]: RGB color in 0-1 range.
"""
r, g, b = color
return r / 255, g / 255, b / 255
@@ -291,15 +272,27 @@ def alpha_extend(color: C3I, alpha: int = 255) -> C4I:
@staticmethod
def alpha_extend_and_scale_to_01(color: C3I, alpha: int = 255) -> C4F:
"""Performs both alpha extension and 0-1 scaling of a color.
- Use this method if you intent to do both to guarantee they
+ Use this method if you intend to do both to guarantee they
are done in correct order.
Args:
- color (Tuple[int, int, int]): A rgb color value.
- alpha (int, optional): The alpha value, 0-255. Defaults to 255.
+ color (Tuple[int, int, int]): RGB color in 0-255 range.
+ alpha (int, optional): Alpha value in 0-255 range. Defaults to 255.
Returns:
- Tuple[int, int, int, int]: A rgba color value, 0-1.
+ Tuple[float, float, float, float]: RGBA color in 0-1 range.
"""
r, g, b = color
return r / 255, g / 255, b / 255, alpha / 255
+
+ @staticmethod
+ def scale_to_255(color: C3F) -> C4I:
+ """Convert RGB (0-1) to RGBA (0-255) for pyglet.shapes.
+
+ Args:
+ color (Tuple[float, float, float]): RGB color in 0-1 range.
+
+ Returns:
+ Tuple[int, int, int, int]: RGBA color in 0-255 range.
+ """
+ return (int(color[0] * 255), int(color[1] * 255), int(color[2] * 255), 255)
diff --git a/tilingsgui/main.py b/tilingsgui/main.py
index 39168ff..361c749 100644
--- a/tilingsgui/main.py
+++ b/tilingsgui/main.py
@@ -1,5 +1,4 @@
-"""Entrypoint.
-"""
+"""Entrypoint."""
import argparse
@@ -17,7 +16,7 @@ def get_args() -> str:
def main() -> None:
"""The application's starting point."""
- app = TilingGui(get_args(), resizable=True)
+ app = TilingGui(get_args(), resizable=True) # type: ignore
app.start()
diff --git a/tilingsgui/menu.py b/tilingsgui/menu.py
index 59186c6..0d73723 100644
--- a/tilingsgui/menu.py
+++ b/tilingsgui/menu.py
@@ -1,6 +1,4 @@
-"""Control stations.
-"""
-
+"""Control stations."""
from typing import Iterable
@@ -22,9 +20,9 @@ class TopMenu(pyglet.event.EventDispatcher, Observer):
_PADDING = 1
_INITIAL_MESSAGE = " -- Basis here -- e.g. 1234_1324"
- _FONT_SIZE = 12
+ _FONT_SIZE = 20
_TEXT_COLOR = Color.alpha_extend(Color.BLACK)
- _TEXT_BOX_COLOR = Color.DARK_GRAY
+ _TEXT_BOX_COLOR = Color.scale_to_01(Color.WHITE)
_BACKGROUND_COLOR = Color.BLACK
_V_BTN = 118
_CTRL_MODIFIER = 18
@@ -194,11 +192,11 @@ class RightMenu(pyglet.event.EventDispatcher, Observer):
"""A menu that sits to the right of the tiling plot."""
_PADDING = 1
- _INITIAL_MESSAGE = "12"
- _FONT_SIZE = 12
+ _INITIAL_MESSAGE = "Req: 12"
+ _FONT_SIZE = 20
_TEXT_COLOR = Color.alpha_extend(Color.BLACK)
- _TEXT_BOX_COLOR = Color.DARK_GRAY
- _BACKGROUND_COLOR = Color.BLACK
+ _TEXT_BOX_COLOR = Color.scale_to_01(Color.WHITE)
+ _BACKGROUND_COLOR = Color.scale_to_01(Color.LIGHT_GRAY)
def __init__(
self,
diff --git a/tilingsgui/resources/img/svg/add_custom.svg b/tilingsgui/resources/img/svg/add_custom.svg
index 06edd7e..9d145e8 100644
--- a/tilingsgui/resources/img/svg/add_custom.svg
+++ b/tilingsgui/resources/img/svg/add_custom.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/add_point.svg b/tilingsgui/resources/img/svg/add_point.svg
index c58fd13..1851242 100644
--- a/tilingsgui/resources/img/svg/add_point.svg
+++ b/tilingsgui/resources/img/svg/add_point.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/export.svg b/tilingsgui/resources/img/svg/export.svg
index 45ea3c4..1226846 100644
--- a/tilingsgui/resources/img/svg/export.svg
+++ b/tilingsgui/resources/img/svg/export.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/factor.svg b/tilingsgui/resources/img/svg/factor.svg
index 8aa063e..6fca35c 100644
--- a/tilingsgui/resources/img/svg/factor.svg
+++ b/tilingsgui/resources/img/svg/factor.svg
@@ -17,4 +17,4 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/factor_int.svg b/tilingsgui/resources/img/svg/factor_int.svg
index 9aa7c3f..280667c 100644
--- a/tilingsgui/resources/img/svg/factor_int.svg
+++ b/tilingsgui/resources/img/svg/factor_int.svg
@@ -18,4 +18,4 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/fusion_c.svg b/tilingsgui/resources/img/svg/fusion_c.svg
index 6fe2189..673e3e0 100644
--- a/tilingsgui/resources/img/svg/fusion_c.svg
+++ b/tilingsgui/resources/img/svg/fusion_c.svg
@@ -7,5 +7,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/tilingsgui/resources/img/svg/fusion_comp_c.svg b/tilingsgui/resources/img/svg/fusion_comp_c.svg
index e946d01..ddb27cb 100644
--- a/tilingsgui/resources/img/svg/fusion_comp_c.svg
+++ b/tilingsgui/resources/img/svg/fusion_comp_c.svg
@@ -8,5 +8,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/tilingsgui/resources/img/svg/fusion_comp_r.svg b/tilingsgui/resources/img/svg/fusion_comp_r.svg
index 6bc84f1..915046c 100644
--- a/tilingsgui/resources/img/svg/fusion_comp_r.svg
+++ b/tilingsgui/resources/img/svg/fusion_comp_r.svg
@@ -8,5 +8,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/tilingsgui/resources/img/svg/fusion_r.svg b/tilingsgui/resources/img/svg/fusion_r.svg
index 68a7f16..301d89f 100644
--- a/tilingsgui/resources/img/svg/fusion_r.svg
+++ b/tilingsgui/resources/img/svg/fusion_r.svg
@@ -7,5 +7,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/tilingsgui/resources/img/svg/htc.svg b/tilingsgui/resources/img/svg/htc.svg
index 470225b..f930712 100644
--- a/tilingsgui/resources/img/svg/htc.svg
+++ b/tilingsgui/resources/img/svg/htc.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/move.svg b/tilingsgui/resources/img/svg/move.svg
index e20cd3a..20fbed8 100644
--- a/tilingsgui/resources/img/svg/move.svg
+++ b/tilingsgui/resources/img/svg/move.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/obs_inf.svg b/tilingsgui/resources/img/svg/obs_inf.svg
index 62b8dba..ed3c951 100644
--- a/tilingsgui/resources/img/svg/obs_inf.svg
+++ b/tilingsgui/resources/img/svg/obs_inf.svg
@@ -12,4 +12,4 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/obstr_trans.svg b/tilingsgui/resources/img/svg/obstr_trans.svg
index fa7c688..38f9f57 100644
--- a/tilingsgui/resources/img/svg/obstr_trans.svg
+++ b/tilingsgui/resources/img/svg/obstr_trans.svg
@@ -25,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/place_east.svg b/tilingsgui/resources/img/svg/place_east.svg
index fa98b92..f0d32e7 100644
--- a/tilingsgui/resources/img/svg/place_east.svg
+++ b/tilingsgui/resources/img/svg/place_east.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/place_north.svg b/tilingsgui/resources/img/svg/place_north.svg
index fe334d3..fec0bd3 100644
--- a/tilingsgui/resources/img/svg/place_north.svg
+++ b/tilingsgui/resources/img/svg/place_north.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/place_south.svg b/tilingsgui/resources/img/svg/place_south.svg
index cbd743c..331805c 100644
--- a/tilingsgui/resources/img/svg/place_south.svg
+++ b/tilingsgui/resources/img/svg/place_south.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/place_west.svg b/tilingsgui/resources/img/svg/place_west.svg
index 819e221..32112c4 100644
--- a/tilingsgui/resources/img/svg/place_west.svg
+++ b/tilingsgui/resources/img/svg/place_west.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/pplace_east.svg b/tilingsgui/resources/img/svg/pplace_east.svg
index 0e90391..52a538b 100644
--- a/tilingsgui/resources/img/svg/pplace_east.svg
+++ b/tilingsgui/resources/img/svg/pplace_east.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/pplace_north.svg b/tilingsgui/resources/img/svg/pplace_north.svg
index 136ceeb..dabbe08 100644
--- a/tilingsgui/resources/img/svg/pplace_north.svg
+++ b/tilingsgui/resources/img/svg/pplace_north.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/pplace_south.svg b/tilingsgui/resources/img/svg/pplace_south.svg
index 3d32f44..a09dbc6 100644
--- a/tilingsgui/resources/img/svg/pplace_south.svg
+++ b/tilingsgui/resources/img/svg/pplace_south.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/pplace_west.svg b/tilingsgui/resources/img/svg/pplace_west.svg
index 59582dc..22998e7 100644
--- a/tilingsgui/resources/img/svg/pplace_west.svg
+++ b/tilingsgui/resources/img/svg/pplace_west.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/pretty.svg b/tilingsgui/resources/img/svg/pretty.svg
index 4d27ce1..b97f2bd 100644
--- a/tilingsgui/resources/img/svg/pretty.svg
+++ b/tilingsgui/resources/img/svg/pretty.svg
@@ -4,4 +4,4 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/redo.svg b/tilingsgui/resources/img/svg/redo.svg
index d905272..c33f9ff 100644
--- a/tilingsgui/resources/img/svg/redo.svg
+++ b/tilingsgui/resources/img/svg/redo.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/rowcolsep.svg b/tilingsgui/resources/img/svg/rowcolsep.svg
index a91f57f..afbaa59 100644
--- a/tilingsgui/resources/img/svg/rowcolsep.svg
+++ b/tilingsgui/resources/img/svg/rowcolsep.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/shading.svg b/tilingsgui/resources/img/svg/shading.svg
index 9eda0f9..1156ff6 100644
--- a/tilingsgui/resources/img/svg/shading.svg
+++ b/tilingsgui/resources/img/svg/shading.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/show_local.svg b/tilingsgui/resources/img/svg/show_local.svg
index d95aaa5..32d8cc6 100644
--- a/tilingsgui/resources/img/svg/show_local.svg
+++ b/tilingsgui/resources/img/svg/show_local.svg
@@ -20,4 +20,4 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/str.svg b/tilingsgui/resources/img/svg/str.svg
index 3f191c0..4a24983 100644
--- a/tilingsgui/resources/img/svg/str.svg
+++ b/tilingsgui/resources/img/svg/str.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/tikz.svg b/tilingsgui/resources/img/svg/tikz.svg
index 2284e29..8a31f30 100644
--- a/tilingsgui/resources/img/svg/tikz.svg
+++ b/tilingsgui/resources/img/svg/tikz.svg
@@ -1,4 +1,4 @@
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/undo.svg b/tilingsgui/resources/img/svg/undo.svg
index 59d5ef9..77000a4 100644
--- a/tilingsgui/resources/img/svg/undo.svg
+++ b/tilingsgui/resources/img/svg/undo.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/resources/img/svg/verification.svg b/tilingsgui/resources/img/svg/verification.svg
index 774b447..229ea80 100644
--- a/tilingsgui/resources/img/svg/verification.svg
+++ b/tilingsgui/resources/img/svg/verification.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/tilingsgui/state.py b/tilingsgui/state.py
index 5ba5aed..dd33f5c 100644
--- a/tilingsgui/state.py
+++ b/tilingsgui/state.py
@@ -1,5 +1,4 @@
-"""A global state for the app, in a seperate module.
-"""
+"""A global state for the app, in a seperate module."""
from typing import Tuple
diff --git a/tilingsgui/tplot.py b/tilingsgui/tplot.py
index 34612f8..7750e5a 100644
--- a/tilingsgui/tplot.py
+++ b/tilingsgui/tplot.py
@@ -1,5 +1,4 @@
-"""The tiling drawing tools.
-"""
+"""The tiling drawing tools."""
import json
from collections import Counter, deque
@@ -13,9 +12,8 @@
from tilings import GriddedPerm, Tiling
from tilings.algorithms import Factor, FactorWithInterleaving
from tilings.exception import InvalidOperationError
-from tilings.strategies import (
+from tilings.strategies import ( # DatabaseVerificationStrategy removed v4.0.0
BasicVerificationStrategy,
- DatabaseVerificationStrategy,
ElementaryVerificationStrategy,
InsertionEncodingVerificationStrategy,
LocallyFactorableVerificationStrategy,
@@ -36,11 +34,19 @@ class TPlot:
REQ_NOT_FOUND: ClassVar[Tuple[int, int, int]] = (-1, -1, -1)
OBS_NOT_FOUND: ClassVar[Tuple[int, int]] = (-1, -1)
- _OBSTRUCTION_COLOR: ClassVar[Tuple[float, float, float]] = Color.RED
- _REQUIREMENT_COLOR: ClassVar[Tuple[float, float, float]] = Color.BLUE
- _HIGHLIGHT_COLOR: ClassVar[Tuple[float, float, float]] = Color.ORANGE
- _EMPTY_COLOR: ClassVar[Tuple[float, float, float]] = Color.GRAY
- _SHADED_CELL_COLOR: ClassVar[Tuple[float, float, float]] = Color.GRAY
+ _OBSTRUCTION_COLOR: ClassVar[Tuple[float, float, float]] = Color.scale_to_01(
+ Color.RED
+ )
+ _REQUIREMENT_COLOR: ClassVar[Tuple[float, float, float]] = Color.scale_to_01(
+ Color.BLUE
+ )
+ _HIGHLIGHT_COLOR: ClassVar[Tuple[float, float, float]] = Color.scale_to_01(
+ Color.ORANGE
+ )
+ _EMPTY_COLOR: ClassVar[Tuple[float, float, float]] = Color.scale_to_01(Color.GRAY)
+ _SHADED_CELL_COLOR: ClassVar[Tuple[float, float, float]] = Color.scale_to_01(
+ Color.GRAY
+ )
_FUZZYNESS = 0.25 # Should be in [0,0.5)
_CLICK_PRECISION_SQUARED: int = 100
_POINT_SIZE = 5
@@ -469,7 +475,7 @@ class TPlotManager(pyglet.event.EventDispatcher, Observer):
_MAX_SEQUENCE_SIZE: ClassVar[int] = 7
_VERIFICATION_STRATS: ClassVar[List[str]] = [
"BasicVerificationStrategy",
- "DatabaseVerificationStrategy",
+ # "DatabaseVerificationStrategy", # Removed in tilings 4.0.0
"ElementaryVerificationStrategy",
"InsertionEncodingVerificationStrategy",
"LocallyFactorableVerificationStrategy",
@@ -492,7 +498,7 @@ def _verify(tiling: Tiling) -> List[str]:
"""
return [
str(BasicVerificationStrategy().verified(tiling)),
- str(DatabaseVerificationStrategy().verified(tiling)),
+ # str(DatabaseVerificationStrategy().verified(tiling)), # Removed
str(ElementaryVerificationStrategy().verified(tiling)),
str(InsertionEncodingVerificationStrategy().verified(tiling)),
str(LocallyFactorableVerificationStrategy().verified(tiling)),
diff --git a/tilingsgui/utils.py b/tilingsgui/utils.py
index f2b4c3c..8624262 100644
--- a/tilingsgui/utils.py
+++ b/tilingsgui/utils.py
@@ -1,5 +1,4 @@
-"""A collection of various utility functionality.
-"""
+"""A collection of various utility functionality."""
import datetime
diff --git a/tilingsgui/widgets.py b/tilingsgui/widgets.py
index 808b00a..72945cd 100644
--- a/tilingsgui/widgets.py
+++ b/tilingsgui/widgets.py
@@ -1,5 +1,4 @@
-"""Graphical UI components.
-"""
+"""Graphical UI components."""
from typing import Callable, ClassVar, Dict, List, Optional, Tuple
@@ -30,10 +29,16 @@ def __init__(self, init_text: str, font_size: int, color: RGBA) -> None:
self._document: pyglet.text.document.UnformattedDocument = (
pyglet.text.document.UnformattedDocument(init_text)
)
- self._document.set_style(0, 0, dict(font_size=font_size, color=color))
+ self._document.set_style(0, 0, {"font_size": font_size, "color": color})
self._layout: pyglet.text.layout.IncrementalTextLayout = (
pyglet.text.layout.IncrementalTextLayout(
- self._document, 0, 0, multiline=False, batch=self._batch
+ self._document,
+ x=0,
+ y=0,
+ width=100,
+ height=20, # Will be updated in position()
+ multiline=False,
+ batch=self._batch,
)
)
self._caret: pyglet.text.caret.Caret = pyglet.text.caret.Caret(self._layout)
@@ -48,10 +53,10 @@ def position(self, x: float, y: float, w: float, h: float) -> None:
w (float): The horizontal length of the component.
h (float): The vertical length of the component.
"""
- self._layout.x = x + Text._LEFT_PAD
- self._layout.y = y
- self._layout.width = w - Text._LEFT_PAD
- self._layout.height = h
+ self._layout.x = int(x + Text._LEFT_PAD)
+ self._layout.y = int(y - 15)
+ self._layout.width = int(w - Text._LEFT_PAD)
+ self._layout.height = int(h)
def set_focus(self) -> None:
"""Set focus on the input text. This is needed to write to it."""
@@ -119,12 +124,14 @@ def __init__(
box_color (Tuple[float, float, float]): The rgb color of the box.
"""
super().__init__(init_text, font_size, text_color)
- self._vertex_list: pyglet.graphics.vertexdomain.VertexList = self._batch.add(
- 4,
- pyglet.gl.GL_QUADS,
- None,
- ("v2f", [0] * 8),
- ("c3B", box_color * 4),
+ box_color_255 = Color.scale_to_255(box_color)
+ self._rectangle = pyglet.shapes.Rectangle(
+ x=0,
+ y=0,
+ width=100,
+ height=20, # Will be updated in position()
+ color=box_color_255,
+ batch=self._batch,
)
def position(self, x: float, y: float, w: float, h: float) -> None:
@@ -137,8 +144,10 @@ def position(self, x: float, y: float, w: float, h: float) -> None:
h (float): The vertical length of the component.
"""
super().position(x, y, w, h)
- for i, vertex in enumerate((x, y, x + w, y, x + w, y + h, x, y + h)):
- self._vertex_list.vertices[i] = vertex
+ self._rectangle.x = x
+ self._rectangle.y = y
+ self._rectangle.width = w
+ self._rectangle.height = h
def hit_test(self, x: float, y: float) -> bool:
"""Is the point (x,y) inside the rectangle that the text box forms.
@@ -151,8 +160,8 @@ def hit_test(self, x: float, y: float) -> bool:
[type]: True iff inside.
"""
return (
- self._vertex_list.vertices[0] < x < self._vertex_list.vertices[2]
- and self._vertex_list.vertices[1] < y < self._vertex_list.vertices[5]
+ self._rectangle.x < x < self._rectangle.x + self._rectangle.width
+ and self._rectangle.y < y < self._rectangle.y + self._rectangle.height
)
@@ -214,9 +223,25 @@ def position(self, x: float, y: float, w: float, h: float) -> None:
h (float): The vertical length of the button.
"""
self._x, self._y, self._w, self._h = x, y, w, h
- dim = min(w, h)
- if dim > 0:
- self._sprite.scale *= dim / self._sprite.width
+ if w > 0 and h > 0:
+ # Scale to fit within the button bounds while maintaining aspect ratio
+ # Handle both AbstractImage and Animation types
+ image = self._sprite.image
+ if hasattr(image, "width") and hasattr(image, "height"):
+ img_width = float(image.width)
+ img_height = float(image.height)
+ else:
+ # Fallback for Animation or other types - use sprite dimensions
+ img_width = float(self._sprite.width)
+ img_height = float(self._sprite.height)
+
+ scale_x = float(w) / img_width
+ scale_y = float(h) / img_height
+ # Use the smaller scale to ensure image fits within bounds
+ scale = (
+ min(scale_x, scale_y) * 0.95
+ ) # 0.95 for slightly smaller, well-proportioned images
+ self._sprite.scale = scale
self._sprite.x = x + self._w / 2 - self._sprite.width / 2
self._sprite.y = y + self._h / 2 - self._sprite.height / 2
@@ -373,7 +398,7 @@ def __contains__(self, key: Tuple[int, int]) -> bool:
class ButtonGrid:
"""A positional object to place and group buttons together."""
- _PADDING: ClassVar[int] = 1
+ _PADDING: ClassVar[int] = 2
def __init__(self, r: int, c: int) -> None:
"""Create a button grid with r rows and c columns.
@@ -413,7 +438,7 @@ def position(self, x: float, y: float, w: float, h: float) -> None:
w (float): The horizontal length of the grid.
h (float): The vertical length of the grid.
"""
- self.rect.x, self.rect.y, self.rect.h, self.rect.h = x, y, w, h
+ self.rect.x, self.rect.y, self.rect.w, self.rect.h = x, y, w, h
self.button_w, self.button_h = w / len(self.buttons[0]), h / len(self.buttons)
self._position_btns()
diff --git a/tox.ini b/tox.ini
index 85c2a40..d2fb7a9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,23 +6,28 @@
[tox]
envlist =
flake8, mypy, pylint, black
- py{37,38,39,310},
- pypy37
+ py{38,39,310,311,312,313},
+ pypy{38,39,310}
[default]
-basepython=python3.8
+basepython=python3.11
[testenv]
description = run test
basepython =
- py37: python3.7
py38: python3.8
py39: python3.9
py310: python3.10
- pypy37: pypy3
+ py311: python3.11
+ py312: python3.12
+ py313: python3.13
+ pypy38: pypy3.8
+ pypy39: pypy3.9
+ pypy310: pypy3.10
deps =
- pytest==7.2.0
- pytest-timeout==2.1.0
+ setuptools
+ pytest>=8.0.0
+ pytest-timeout>=2.3.0
commands = pytest
[pytest]
@@ -34,8 +39,8 @@ description = run flake8 (linter)
basepython = {[default]basepython}
skip_install = True
deps =
- flake8==5.0.4
- flake8-isort==5.0.0
+ flake8>=7.0.0
+ flake8-isort>=6.0.0
commands =
flake8 --isort-show-traceback tilingsgui tests setup.py
@@ -43,19 +48,19 @@ commands =
description = run pylint (static code analysis)
basepython = {[default]basepython}
deps =
- pylint==2.15.5
+ pylint>=3.0.0
commands = pylint tilingsgui
[testenv:mypy]
description = run mypy (static type checker)
basepython = {[default]basepython}
deps =
- mypy==0.990
+ mypy>=1.13.0
commands = mypy
[testenv:black]
description = check that comply with autoformating
basepython = {[default]basepython}
deps =
- black==22.10.0
+ black>=24.0.0
commands = black --check --diff .