Python Forum
Libraries/Tools to create an editor similar to Tiled
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Libraries/Tools to create an editor similar to Tiled
#9
This is an app written with Qt. It uses a custom button that draws an image as the button background. Multiple tile buttons create a TileControl that uses a signal to call a function when one of the image buttons is pressed.
"""Mosaic image generator"""
import sys
import math
import pathlib
from functools import partial
from PIL import Image, ImageQt
from PySide2.QtWidgets import QApplication, QPushButton, QWidget, QDialog, QGridLayout, QVBoxLayout
from PySide2.QtGui import QPainter, QPainterPath, QCursor
from PySide2.QtCore import Qt, QRect, Signal

TILE_SIZE = (60, 60)
TILE_RECT = QRect(2, 2, 56, 56)

class Tile(QPushButton):
    """A button for displaying images"""
    def __init__(self, parent):
        super().__init__(parent)
        self.setFixedSize(*TILE_SIZE)
        self.image = None
        self.image_file = None

    def paintEvent(self, _):
        """Paint widget.  Fill with image or draw a rounded rectangle"""
        painter = QPainter(self)
        if self.image:
            painter.drawImage(0, 0, ImageQt.ImageQt(self.image))
        else:
            path = QPainterPath()
            path.addRoundedRect(TILE_RECT, 4, 4)
            painter.fillPath(path, Qt.lightGray)
        painter.end()

    def load_image(self, image):
        """Load image from a file"""
        self.image_file = image
        if self.image_file:
            self.image = Image.open(image).resize(TILE_SIZE)
        else:
            self.image = None
        self.update()

class TileControl(QWidget):
    """A matrix of tiles.  Use clicked signal to be notified when a tile is selected"""

    clicked = Signal(Tile)

    def __init__(self, parent, rows, columns):
        super().__init__(parent)
        layout = QGridLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setHorizontalSpacing(0)
        layout.setVerticalSpacing(0)
        self.rows = rows
        self.columns = columns
        self.tiles = []
        for row in range(rows):
            for column in range(columns):
                tile = Tile(self)
                tile.clicked.connect(partial(self.select_tile, tile))
                layout.addWidget(tile, row, column)
                self.tiles.append(tile)

    def select_tile(self, tile):
        """Callback when tile is pressed.  Send signal with tile as arg"""
        self.clicked.emit(tile)


class TileDialog(QDialog):
    """Dialog for selecting a tile"""
    def __init__(self, images):
        super().__init__()
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.tile = None
        tile_count = len(images) + 1
        columns = int(math.ceil(math.sqrt(tile_count)))
        rows = int(math.ceil(tile_count / columns))
        self.tiles = TileControl(self, rows, columns)
        for tile, image in zip(self.tiles.tiles, images):
            tile.load_image(image)
        self.tiles.clicked.connect(self.select_tile)
 
        reject_button = QPushButton('Cancel')
        reject_button.clicked.connect(self.reject)

        self.layout = QVBoxLayout(self)
        self.layout.setContentsMargins(2, 2, 2, 2)
        self.layout.setSpacing(2)
        self.layout.addWidget(self.tiles)
        self.layout.addWidget(reject_button)

    def select_tile(self, tile):
        """Tile was selected.  Save and accept"""
        self.tile = tile
        self.accept()

class TileWindow(QWidget):
    """Make a pretty mosaic.  Set tile images by selecting image from dialog
    that pops up when tile is pressed.
    def"""
    def __init__(self, rows, columns, images):
        super().__init__()
        self.dialog = TileDialog(images)

        self.tiles = TileControl(self, rows, columns)
        self.tiles.clicked.connect(self.select_tile)

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.tiles)

    def select_tile(self, tile):
        """A tile was pressed.  Get an image from the dialog"""
        self.dialog.move(QCursor.pos())
        if self.dialog.exec_():
            tile.load_image(self.dialog.tile.image_file)

# Get list of image files in same directory as program
PATH = pathlib.Path(sys.path[0])
IMAGES = [PATH/file for file in PATH.iterdir() if file.suffix in ('.png', '.PNG', '.jpg', '.JPG')]

APP = QApplication()
WINDOW = TileWindow(10, 10, IMAGES)
WINDOW.show()
sys.exit(APP.exec_())
This was a good excuse to learn more about painting in Qt. I'm not sure if I can work up enough interest to stitch all the images together to make a new image.
Reply


Messages In This Thread
RE: Libraries/Tools to create an editor similar to Tiled - by deanhystad - May-11-2021, 05:35 AM

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020