QPixmap的图块(图像)具有重叠的区域

时间:2019-08-14 20:19:46

标签: python image pyqt5 qpixmap

我想创建一个带有图像的按钮网格。每个按钮上的图像都是从较大的图像中切下来的,该图像被切成碎片-因此按钮网格可以看作是原始图像的图块(我用来在按钮上放置图像的代码是from here

要将图像(image=QPixmap(path_to_image))切成碎片,我在for循环中使用了方法image.copy(x, y, width, height)

起初,我认为代码可以正常工作。更精确的检查表明图像片段有重叠部分。

这是经过GIF图片测试的代码:

import os
import sys

from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import \
    QWidget, QApplication, \
    QGridLayout, \
    QLabel, QPushButton

def cut_image_into_tiles(image, rows=4, cols=4) -> dict:
    if isinstance(image, str) and os.path.exists(image):
        image = QPixmap(image)
    elif not isinstance(image, QPixmap):
        raise ValueError("image must be str or QPixmap object")

    # Dim of the tile
    tw = int(image.size().width() / cols)
    th = int(image.size().height() / rows)

    # prepare return value
    tiles = {"width": tw, "height": th}
    for r in range(rows):
        for c in range(cols):
            tile = image.copy(c * th, r * tw, tw, th)
            # args: x, y, width, height
            # https://doc.qt.io/qt-5/qpixmap.html#copy-1
            tiles[(r, c)] = tile

    return tiles


def create_pixmapbutton(pixmap, width=0, height=0) -> QPushButton:
    if isinstance(pixmap, QPixmap):
        button = QPushButton()
        if width > 0 and height > 0:
            button.setIconSize(QSize(width, height))
        else:
            button.setIconSize(pixmap.size())
        button.setIcon(QIcon(pixmap))
        return button


def run_viewer(imagepath, rows=4, cols=4):

    # ImageCutter.run_viewer(imagepath)
    app = QApplication(sys.argv)

    image = QPixmap(imagepath)

    tiles = cut_image_into_tiles(image=image, rows=rows, cols=cols)
    tilewidth = tiles["width"]
    tileheight = tiles["height"]
    # get dict tiles (keys:=(row, col) or width or height)

    viewer = QWidget()
    layout = QGridLayout()
    viewer.setLayout(layout)
    viewer.setWindowTitle("ImageCutter Viewer")

    lbl = QLabel()
    lbl.setPixmap(image)
    layout.addWidget(lbl, 0, 0, rows, cols)

    for r in range(rows):
        for c in range(cols):
            btn = create_pixmapbutton(
                tiles[r, c], width=tilewidth, height=tileheight)

            btninfo = "buttonsize={}x{}".format(tilewidth, tileheight)
            btn.setToolTip(btninfo)
            # logger.debug("    create button [{}]".format(btninfo))
            layout.addWidget(btn, r, cols + c)

    viewer.show()
    sys.exit(app.exec_())

我用run_viewer(path_to_a_gif_image, rows=3, cols=3)

测试了代码

3 个答案:

答案 0 :(得分:1)

尝试一下:

import sys
from PyQt5.QtCore import QSize, QRect
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import (QWidget, QApplication, QGridLayout, 
                             QLabel, QPushButton, QSizePolicy)
from PIL import Image


def cut_image_into_tiles(image, r=4, c=4):
    im = Image.open(image)                                                           
    x, y = im.size 
    for i in range(r):
        for j in range(c):
            if i!=r and j!=c:
                im.crop(box=(x/r*i, y/c*j, x/r*(i+1)-1, y/c*(j+1)-1)).\
                save('image{}{}.png'.format(str(i+1), str(j+1)))      


def run_viewer(imagepath, rows=4, cols=4):
    app = QApplication(sys.argv)
    image = QPixmap(imagepath)

    tiles = cut_image_into_tiles(image=imagepath, r=rows, c=cols)

    viewer = QWidget()
    layout = QGridLayout()
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(0)
    viewer.setLayout(layout)
    viewer.setWindowTitle("ImageCutter Viewer")

    lbl = QLabel()
    lbl.setPixmap(image)
    layout.addWidget(lbl, 0, 0, rows, cols)

    for r in range(1, rows+1):
        for c in range(1, cols+1):    
            button = QPushButton()
            button.setStyleSheet('''QPushButton {background-color: yellow; border: 1px solid black;}
                                    QPushButton:pressed {background-color: green;}''')
            image = QPixmap(f"image{c}{r}.png")
            button.setIconSize(image.size())
            button.setIcon(QIcon(image))            
            layout.addWidget(button, r-1, c+cols)            

    viewer.show()
    sys.exit(app.exec_())


run_viewer("linux.gif", rows=3, cols=3)

enter image description here

答案 1 :(得分:1)

每个网格的宽度必须取决于图像的宽度,在这种情况下,它取决于th,但是th取决于高度,这是不正确的,您必须将th更改为tw,高度也应更改为相同。

考虑到上述情况,解决方案是:

import os
import sys

from PyQt5.QtCore import QSize
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QLabel, QPushButton


def cut_image_into_tiles(image, rows=4, cols=4) -> dict:
    if isinstance(image, str) and os.path.exists(image):
        image = QPixmap(image)
    elif not isinstance(image, QPixmap):
        raise ValueError("image must be str or QPixmap object")

    # Dim of the tile
    tw = int(image.size().width() / cols)
    th = int(image.size().height() / rows)

    # prepare return value
    tiles = {"width": tw, "height": th}
    for r in range(rows):
        for c in range(cols):
            tile = image.copy(c * tw, r * th, tw, th) # <----
            # args: x, y, width, height
            # https://doc.qt.io/qt-5/qpixmap.html#copy-1
            tiles[(r, c)] = tile

    return tiles


def create_pixmapbutton(pixmap, width=0, height=0) -> QPushButton:
    if isinstance(pixmap, QPixmap):
        button = QPushButton()
        if width > 0 and height > 0:
            button.setIconSize(QSize(width, height))
        else:
            button.setIconSize(pixmap.size())
        button.setIcon(QIcon(pixmap))
        button.setContentsMargins(0, 0, 0, 0)
        button.setFixedSize(button.iconSize())
        return button


def run_viewer(imagepath, rows=4, cols=4):

    # ImageCutter.run_viewer(imagepath)
    app = QApplication(sys.argv)

    image = QPixmap(imagepath)

    tiles = cut_image_into_tiles(image=image, rows=rows, cols=cols)
    tilewidth = tiles["width"]
    tileheight = tiles["height"]
    # get dict tiles (keys:=(row, col) or width or height)

    viewer = QWidget()
    layout = QGridLayout(viewer)
    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(0)
    viewer.setWindowTitle("ImageCutter Viewer")

    lbl = QLabel()
    lbl.setPixmap(image)
    layout.addWidget(lbl, 0, 0, rows, cols)

    for r in range(rows):
        for c in range(cols):
            btn = create_pixmapbutton(tiles[r, c], width=tilewidth, height=tileheight)

            btninfo = "buttonsize={}x{}".format(tilewidth, tileheight)
            btn.setToolTip(btninfo)
            # logger.debug("    create button [{}]".format(btninfo))
            layout.addWidget(btn, r, cols + c)

    viewer.show()
    viewer.setFixedSize(viewer.sizeHint())
    sys.exit(app.exec_())


run_viewer("linux.gif", 3, 3)

答案 2 :(得分:0)

好吧,我发现对于大图像和大网格,右侧按钮上的图像可以小于其他按钮。底部按钮相同。

顶部和左侧的偏移量可以防止这种情况。

因此,函数def cut_image_into_tiles(image, rows=4, cols=4) -> dict:必须适用于:

def cut_image_into_tiles(image, rows=4, cols=4) -> dict:
    if isinstance(image, str) and os.path.exists(image):
        image = QPixmap(image)
    elif not isinstance(image, QPixmap):
        raise ValueError("image must be str or QPixmap object")

    # Dim of tiled images
    width = image.size().width()
    height = image.size().height()
    tw = int(width / cols)
    th = int(height / rows)
    offset_w = int((width - tw * cols)/2)    # !!!
    offset_h = int((height - th * rows)/2)   # !!!

    # prepare return value
    tiles = {"width": tw, "height": th}
    for r in range(rows):
        for c in range(cols):
            x = c * tw
            y = r * th
            if c == 0:
                x += offset_w    # !!!
            if r == 0:
                y += offset_h    # !!!
            tile = image.copy(x, y, tw, th)
            # args: x, y, width, height
            # https://doc.qt.io/qt-5/qpixmap.html#copy-1
            tiles[(r, c)] = tile

    return tiles

整个代码现在是:

def cut_image_into_tiles(image, rows=4, cols=4) -> dict:
    if isinstance(image, str) and os.path.exists(image):
        image = QPixmap(image)
    elif not isinstance(image, QPixmap):
        raise ValueError("image must be str or QPixmap object")

    # Dim of tiled images
    width = image.size().width()
    height = image.size().height()
    tw = int(width / cols)
    th = int(height / rows)
    offset_w = int((width - tw * cols)/2)
    offset_h = int((height - th * rows)/2)

    # prepare return value
    tiles = {"width": tw, "height": th}
    for r in range(rows):
        for c in range(cols):
            x = c * tw
            y = r * th
            if c == 0:
                x += offset_w
            if r == 0:
                y += offset_h
            tile = image.copy(x, y, tw, th)
            # args: x, y, width, height
            # https://doc.qt.io/qt-5/qpixmap.html#copy-1
            tiles[(r, c)] = tile

    return tiles


def create_pixmapbutton(pixmap, width=0, height=0) -> QPushButton:
    if isinstance(pixmap, QPixmap):
        button = QPushButton()
        if width > 0 and height > 0:
            button.setIconSize(QSize(width, height))
        else:
            button.setIconSize(pixmap.size())
        button.setIcon(QIcon(pixmap))
        return button


def run_viewer(imagepath, rows=4, cols=4):

    # ImageCutter.run_viewer(imagepath)
    app = QApplication(sys.argv)

    image = QPixmap(imagepath)

    tiles = cut_image_into_tiles(image=image, rows=rows, cols=cols)
    tilewidth = tiles["width"]
    tileheight = tiles["height"]
    # get dict tiles (keys:=(row, col) or width or height)

    viewer = QWidget()
    layout = QGridLayout()
    viewer.setLayout(layout)
    viewer.setWindowTitle("ImageCutter Viewer")

    lbl = QLabel()
    lbl.setPixmap(image)
    layout.addWidget(lbl, 0, 0, rows, cols)

    for r in range(rows):
        for c in range(cols):
            btn = create_pixmapbutton(
                tiles[r, c], width=tilewidth, height=tileheight)

            btninfo = "buttonsize={}x{}".format(tilewidth, tileheight)
            btn.setToolTip(btninfo)
            # logger.debug("    create button [{}]".format(btninfo))
            layout.addWidget(btn, r, cols + c)

    viewer.show()
    sys.exit(app.exec_())