【问题标题】:Load image on python CheckListCtrl with transparency在 python CheckListCtrl 上加载具有透明度的图像
【发布时间】:2018-06-19 21:50:32
【问题描述】:

我开始在 python 上使用 WX GUI,在我的“Hello Word”项目中,我正在尝试创建一个能够读取任何图像并将其作为图标显示到 CheckListCtrl 列中的程序。我已经完成了第一部分(读取图像并将其绘制到 CheckListCtrl 中),但我无法加载 PNG 图像并保持该图标的透明度。

我的代码如下:

'''
17 June 2018
@autor: Daniel Carrasco
'''

import wx
from wx.lib.mixins.listctrl import CheckListCtrlMixin, ListCtrlAutoWidthMixin
import sys
from pathlib import Path

BACKGROUNDCOLOR = (240, 240, 240, 255);

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):
  def __init__(self, parent):
    wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER,
              size=wx.Size(395, 467), pos=wx.Point(10, 20));
    CheckListCtrlMixin.__init__(self);
    ListCtrlAutoWidthMixin.__init__(self);

#====================================================================
class MainFrame(wx.Frame):
  def __init__(self, *args, **kwargs):
    self.dataFolder = {
      "images": Path("images/")
    }

    wx.Frame.__init__(self, *args, **kwargs);

    icon = wx.Icon("icons.ico", wx.BITMAP_TYPE_ICO)
    self.SetIcon(icon);

    self.createWidgets();
    self.createButtons();
    self.Show();


  #----------------------------------------------------------
  def exitGUI(self, event):       # callback
    self.Destroy();

  #----------------------------------------------------------
  def createWidgets(self):
    self.CreateStatusBar();      # wxPython built-in method
    self.createMenu();

    # Creamos el panel
    boxSizer = wx.BoxSizer();

    panel = wx.Panel(self);
    panel.SetBackgroundColour(BACKGROUNDCOLOR);
    panel.SetSizerAndFit(boxSizer);

    staticBox = wx.StaticBox( panel, -1, "Listado de Saves", size=(415, 500),
              pos=wx.Point(5, 0) )   
    self.statBoxSizerV = wx.StaticBoxSizer(staticBox, wx.VERTICAL)

    # Lista de items
    self.itemList = CheckListCtrl(staticBox);
    self.itemList.InsertColumn(0, '', width=32);
    self.itemList.InsertColumn(1, 'Icono', width=52);
    self.itemList.InsertColumn(2, 'Título', width=140);

    self.il_Small = self.itemList.GetImageList(wx.IMAGE_LIST_SMALL);
    self.il = wx.ImageList(48, 48, wx.IMAGE_LIST_SMALL);
    self.itemList.SetImageList(self.il, wx.IMAGE_LIST_SMALL);

    image = wx.Image(str(self.dataFolder["images"] / "tick_1.png"), wx.BITMAP_TYPE_ANY);
    self.il.Add(wx.Bitmap(image));

    image = wx.Image(str(self.dataFolder["images"] / 'tick_2.png'), wx.BITMAP_TYPE_ANY);
    self.il.Add(wx.Bitmap(image));

    image = wx.Image(str(self.dataFolder["images"] / 'exit.png'), wx.BITMAP_TYPE_ANY );
    for x in range(0, 4):
      for y in range(0, 4):
        image.SetAlpha(x, y, 0);
    image = image.Scale(40, 40, wx.IMAGE_QUALITY_HIGH);
    image = image.Size(wx.Size(48,48), wx.Point(4,4), 255, 255, 255);
#    image.ClearAlpha();
    self.il.Add(wx.Bitmap(image));

    image = wx.Image(str(self.dataFolder["images"] / 'test.png'), wx.BITMAP_TYPE_ANY );
    image = image.Scale(40, 40, wx.IMAGE_QUALITY_HIGH);
    image = image.Size(wx.Size(48,48), wx.Point(4,4), 255, 255, 255);
    self.il.Add(image.ConvertToBitmap());

    index = self.itemList.InsertItem(sys.maxsize, "test");
    self.itemList.SetItemColumnImage(0, 1, 3)
    #self.itemList.Append("Prueba");

  #----------------------------------------------------------
  def createButtons(self):
    pass

  #----------------------------------------------------------
  def createMenu(self):
    # Menú Archivo
    APP_EXIT = 1;
    mArchivo = wx.Menu();
    qmi = wx.MenuItem(mArchivo, APP_EXIT, '&Salir\tCtrl+Q');
    image = wx.Image(str(self.dataFolder["images"] / 'exit.png'),wx.BITMAP_TYPE_PNG);
    image = image.Scale(16, 16, wx.IMAGE_QUALITY_HIGH);
    qmi.SetBitmap(image.ConvertToBitmap());
    mArchivo.Append(qmi);
    self.Bind(wx.EVT_MENU, self.exitGUI, id=APP_EXIT);

    # Barra de menús
    menuBar = wx.MenuBar();

    menuBar.Append(mArchivo, "&Archivo");

    # Seteamos la barra de menús
    self.SetMenuBar(menuBar);


#======================
# Start GUI
#======================
app = wx.App()
MainFrame(None, style= wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX, title="Savegame Linker", size=(485,587))
app.MainLoop()

此代码只是将图像读入 Image 对象,然后缩放图像并添加边框调整大小。问题是PNG透明度没有保留,只有边框是透明的:

如果我用 Photoshop 删除图像透明度(添加白色背景),则图像会以我想要的透明度显示:

有什么方法可以保持 CheckListCtrl 上的 PNG 透明度,或者至少为图像添加一个白色背景(这看起来像是一种替代解决方案)。如果可能只使用 WX,我想这样做,因为我认为使用枕头模块(例如,仅用于消除透明度)不是最佳解决方案。

谢谢!!

【问题讨论】:

    标签: python python-3.x wxpython


    【解决方案1】:

    我不知道您是如何运行该代码的,但尽我所能尝试,我无法这样做,因此无法确定下面的答案。
    wx.Image 具有一系列“图像处理程序”,其中,默认情况下仅加载 BMPHandler。我怀疑您需要在尝试 SetAlpha 之前加载 PNGHandler 并且您应该事先使用 HasAlpha 检查图像。
    见:https://docs.wxpython.org/wx.Image.html

    Alpha 通道支持
    从 wxWidgets 2.5.0 开始,wx.Image 支持 alpha 通道数据,即除了每个像素的红色、绿色和蓝色分量的一个字节之外,它还存储一个表示像素不透明度的字节。 alpha 值 0 对应于透明像素(空不透明度),而值 255 表示像素 100% 不透明。常量 IMAGE_ALPHA_TRANSPARENT 和 IMAGE_ALPHA_OPAQUE 可用于以更易读的形式指示这些值。虽然所有图像都有 RGB 数据,但并非所有图像都有 Alpha 通道。在使用 wx.Image.GetAlpha 之前,您应该检查此图像是否包含带有 wx.Image.HasAlpha 的 alpha 通道。目前 BMP、PNG、TGA 和 TIFF 格式处理程序对加载具有完整的 alpha 通道支持,因此如果您想使用 alpha,您必须使用其中一种格式。如果您使用 wx.Image.SetAlpha 自己初始化图像 alpha 通道,则应将其保存为 PNG、TGA 或 TIFF 格式以避免丢失,因为这些是当前唯一支持使用 alpha 保存的处理程序。

    可用的图像处理程序以下图像处理程序是 可用的。 BMPHandler 始终默认安装。要使用其他 图像格式,安装适当的处理程序 wx.Image.AddHandler 或调用 wx.InitAllImageHandlers 。

    BMPHandler:始终用于加载(包括 alpha 支持)和保存 已安装。
    wx.PNGHandler:用于加载和保存。包括阿尔法 支持。
    wx.JPEGHandler:用于加载和保存。
    wx.GIFHandler:用于加载和保存(见下文)。
    wx.PCXHandler:用于加载和保存(见下文)。
    wx.PNMHandler:用于加载和保存(见下文)。
    wx.TIFFHandler:用于加载和保存。包括 Alpha 版支持。
    wx.TGAHandler:用于加载和保存。包括 Alpha 版支持。
    wx.IFFHandler:仅用于加载。 wx.XPMHandler:用于加载和保存。
    ICOHandler:用于加载和保存。 CURHandler:用于加载和保存。
    ANIHandler:仅用于加载。
    以PCX格式保存时,wx.PCXHandler会统计图片中不同颜色的个数;如果有 256 种或更少的颜色,它将保存为 8 位,否则它将>保存为 24 位。加载 PNM 仅适用于 ASCII 或原始 RGB 图像。 当以 PNM 格式保存时,wx.PNMHandler 将始终保存为原始 RGB。 保存 GIF 需要最大 8 bpp 的图像(请参阅 Quantize ),并且 alpha 通道转换为遮罩(请参阅 wx.Image.ConvertAlphaToMask )。 保存动画 GIF 需要相同大小的图像(请参阅 wx.GIFHandler.SaveAnimation)

    另见 wx.Bitmap、wx.InitAllImageHandlers、PixelData

    【讨论】:

    • 我已经测试了处理程序,但行为相同。我已经尝试了很多东西,比如测试是否有 alpha 通道(true),获取渐变区域(从 0 到 255)上具有不同值的一些像素的 alpha 数据,移除 alpha 通道(然后整个框变成红色),并使用内置函数将 alpha 转换为掩码......这就是我问的原因,我已经搜索了大约两天并测试了很多东西,但我不知道还能尝试什么。我会再次尝试使用面具。
    【解决方案2】:

    我认为正确的方法是将透明度转换为蒙版。我已经对其进行了测试,但看起来我已经使用了其他导致失败的功能。

    image = wx.Image(str(self.dataFolder["images"] / 'test.png'), wx.BITMAP_TYPE_ANY );
    image = image.Scale(40, 40, wx.IMAGE_QUALITY_HIGH);
    image.ConvertAlphaToMask(threshold=50);
    image = image.Size(wx.Size(48,48), wx.Point(4,4), 255, 255, 255);
    self.il.Add(image.ConvertToBitmap());
    

    我最后一次尝试该函数是在 image.Size 函数之后,然后它失败了(也许 Size 函数删除了透明度),但如果在此之前完成,则可以。

    再次感谢和问候!!

    编辑:

    几个月后,我继续这个项目,我找到了另一种使背景透明的方法:删除透明度,将其转换为纯白色:

    def remove_transparency(im, bg_colour=(255, 255, 255)):
        # Only process if image has transparency (http://stackoverflow.com/a/1963146)
        if im.mode in ('RGBA', 'LA') or (im.mode == 'P' and 'transparency' in im.info):
                # Need to convert to RGBA if LA format due to a bug in PIL (http://stackoverflow.com/a/1963146)
                alpha = im.convert('RGBA').split()[-1]
                # Create a new background image of our matt color.
                # Must be RGBA because paste requires both images have the same format
                # (http://stackoverflow.com/a/8720632    and    http://stackoverflow.com/a/9459208)
                bg = Image.new("RGBA", im.size, bg_colour + (255,))
                bg.paste(im, mask=alpha)
                return bg
    
        else:
                return im
    
    # Open the image
    sbuf = BytesIO(campo[4])
    im = Image.open(sbuf)
    
    # Remove transparency (white background will be transparent on ImageList)
    im2 = remove_transparency(im).convert("RGB")
    im.close()
    
    # Create an wx.Image from image
    width, height = im2.size
    image = wx.Image(width, height, im2.tobytes())
    image = image.Size(wx.Size(48,48), wx.Point(2,2), 255, 255, 255)
    
    # Convert it to Bitmap and add it to ImageList
    image = image.ConvertToBitmap()
    icon_image = self.il.Add(image)
    sbuf.close()
    

    我稍微改变了存储图像的方式,现在它们来自存储在 SQLite DB BLOB (campo[4]) 上的 PNG。

    你好!!

    【讨论】:

      猜你喜欢
      • 2013-02-03
      • 2020-10-17
      • 2010-12-22
      • 1970-01-01
      • 2018-02-16
      • 1970-01-01
      • 1970-01-01
      • 2020-10-07
      • 2012-08-28
      相关资源
      最近更新 更多