【问题标题】:wxPython - Resizing an image with the EVT_SIZE event of its parent panelwxPython - 使用其父面板的 EVT_SIZE 事件调整图像大小
【发布时间】:2010-11-12 10:56:05
【问题描述】:

我之前问这个问题的尝试很糟糕,我也取得了一些进步,请多多包涵,我不打算重复问这么多次,这不是我的风格。

这是最终版本:我正在调整一个包含 DC 客户端绘制位图的窗口的大小,并且在 EVT_SIZE 事件中,我通过重新缩放(使用缩放,而不是重新缩放)并重新绘制图像来调整它的大小。问题是它似乎并不尊重纵横比,即使我正在为它计算 w/h。此外,当它的高度增加时,图像会失真。最后,当另一个窗口经过它时,图像变为白色。任何想法如何解决这些问题?我的窗口/图像类如下:

class TransactionImage(wx.Window):
    def __init__(self, parent, fname, name):
        wx.Window.__init__(self, parent, name=name)

        self.dc = wx.ClientDC(self)  

        self.load_image(fname)
        cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
        self.SetCursor(cursor)

        self.Bind(wx.EVT_SIZE, self.resize_space)


    def load_image(self, image):
        self.image = wx.Image(image, wx.BITMAP_TYPE_JPEG)
        (w, h) = self.image.GetSize()
        self.image_ar = w/h

    def resize_space(self, size):
        (w, h) = self.get_best_size()
        self.s_image = self.image.Scale(w, h)
        self.bitmap = wx.BitmapFromImage(self.s_image)
        self.dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)
        # how can I 'refresh this area to make it 'fit'

    def get_best_size(self):
        (window_width, window_height) = self.GetSizeTuple()
        new_height = window_width / self.image_ar
        new_size = (window_width, new_height)
        return new_size

另外,我无法理解如何正确使用客户端 DC。我想在重新绘制下一个图像之前刷新窗口区域,因为如果我不这样做,我会得到奇怪的 risiduals,它看起来很糟糕。为了解决这个问题,我尝试使用 dc.Clear 来清除背景。但是,在每次调整大小时都这样做,因为我需要在重新调整大小时使图像闪烁一百万次。我怎样才能避免这种情况?

编辑 -

针对 Umyal 的评论回复 - 这是我的应用程序的一个非常简化的版本。无论哪种方式,我都为图像分类我的窗口生成器,大小处理程序重新缩放图像会导致图像严重闪烁,从而产生不吸引人的伪像。此外,当另一帧经过应用程序时,图像显示变为白色,就像被擦除一样。

我在想办法解决这个问题 - 我可以实现 windows 图像查看器似乎有的解决方案,即只有当用户在调整图像大小时放开框架边缘时,图像才会重新缩放和重新绘制。该解决方案的问题是没有明确的方法来检测用户何时停止调整框架的大小。 (wxEVT_SIZE, wxEVT_SIZING)

这是简化的应用程序代码,您需要找到自己的图像,并且越大越好。原始图像尺寸为 3872 x 2592

# this is required for 'real' math - derive the 'aspect ratio'
from __future__ import division
import wx

class TransactionImage(wx.Window):
    def __init__(self, parent, fname, name):
        wx.Window.__init__(self, parent, name=name)

        self.load_image(fname)
        cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
        self.SetCursor(cursor)

        self.Bind(wx.EVT_SIZE, self.resize_space)
        self.Bind(wx.EVT_PAINT, self.on_paint)

    def load_image(self, image):
        self.image = wx.Image(image, wx.BITMAP_TYPE_JPEG)
        (w, h) = self.image.GetSize()
        self.image_ar = w/h
        self.bitmap = wx.BitmapFromImage(self.image)

    def resize_space(self, event):
        (w, h) = self.get_best_size()
        self.s_image = self.image.Scale(w, h)
        self.bitmap = wx.BitmapFromImage(self.s_image)

    def on_paint(self, event):
        self.dc = wx.PaintDC(self)
        self.dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)

    def get_best_size(self):
        (window_width, window_height) = self.GetSizeTuple()
        new_height = window_width / self.image_ar
        new_size = (window_width, new_height)
        return new_size


class OriginalTransactionImage(wx.Window):
    def __init__(self, parent, fname, name):
        wx.Window.__init__(self, parent, name=name)

        self.dc = wx.ClientDC(self)  

        self.load_image(fname)
        cursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
        self.SetCursor(cursor)

        self.Bind(wx.EVT_SIZE, self.resize_space)


    def load_image(self, image):
        self.image = wx.Image(image, wx.BITMAP_TYPE_JPEG)
        (w, h) = self.image.GetSize()
        self.image_ar = w/h

    def resize_space(self, size):
        (w, h) = self.get_best_size()
        self.s_image = self.image.Scale(w, h)
        self.bitmap = wx.BitmapFromImage(self.s_image)
        self.dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)

    def get_best_size(self):
        (window_width, window_height) = self.GetSizeTuple()
        new_height = window_width / self.image_ar
        new_size = (window_width, new_height)
        return new_size


class ImageBrowser(wx.Frame):

    def __init__(self, image1, image2, parent=None, id=wx.ID_ANY,
                 pos=wx.DefaultPosition, title='Image Browser'):
        size = (1500, 800)
        wx.Frame.__init__(self, parent, id, title, pos, size)

        self.CentreOnScreen()

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panel.SetBackgroundColour(wx.Colour(191,197,229))

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)

        self.image_panel = wx.Panel(self.panel, wx.ID_ANY, style=wx.SIMPLE_BORDER)
        self.image_panel.SetBackgroundColour(wx.Colour(255, 255, 255))

        self.image_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.image_panel.SetSizer(self.image_sizer)

        self.load_image_sizer(image1, image2)
        self.main_sizer.Add(self.image_panel, 1, wx.GROW|wx.ALIGN_CENTER|wx.ALL, 25)
        self.panel.SetSizer(self.main_sizer)


    def load_image_sizer(self, image1, image2):
        #bitmap1 = OriginalTransactionImage(self.image_panel, image1, 'image1')
        #bitmap2 = OriginalTransactionImage(self.image_panel, image2, 'image2')

        bitmap1 = TransactionImage(self.image_panel, image1, 'image1')
        bitmap2 = TransactionImage(self.image_panel, image2, 'image2')

        self.image_sizer.Add(bitmap1, 1, wx.GROW|wx.ALIGN_LEFT|wx.ALL, 20)
        self.image_sizer.Add(bitmap2, 1, wx.GROW|wx.ALIGN_RIGHT|wx.ALL, 20)


class IBApp(wx.App):

    def OnInit(self):
        img1 = "0_3126_image1.jpeg"
        img2 = "0_3126_image2.jpeg"

        ib = ImageBrowser(img1, img2)
        ib.Show()
        self.SetTopWindow(ib)        
        return True

app = IBApp(False, None)
app.MainLoop()

【问题讨论】:

    标签: image wxpython resize-image


    【解决方案1】:

    不要在窗口实例中保留对客户端 DC 的引用(http://docs.wxwidgets.org/2.6/wx_wxclientdc.html),这也不是绘制窗口 dc 的正确方法

    而是绑定到 PAINT_EVENT 并在那里绘制,下面我展示了你所说的添加到你的类中的东西

    class TransactionImage(wx.Window):
        def __init__(self, parent, fname, name):
    
            self.Bind(wx.EVT_SIZE, self.resize_space)
            self.Bind(wx.EVT_PAINT, self.onpaint)
    
        def onpaint(self):
            dc = wx.PaintDC(self)
            dc.DrawBitmap(self.bitmap, 0, 0, useMask=False)
    
        def resize_space(self, size):
            (w, h) = self.get_best_size()
            self.s_image = self.image.Scale(w, h)
            self.bitmap = wx.BitmapFromImage(self.s_image)
            self.Refresh()
    

    【讨论】:

    • 这是一个糟糕的建议,有几个原因。首先,我的做法不可能是“完全错误的”,因为经过一些测试,我发现它实际上比您提供的代码执行得更好。其次,它没有解决我提到的问题,即尺寸事件和重绘导致图像不断闪烁,这是一个不吸引人的副作用。此外,在另一帧经过图像后,图像仍会变白。为什么?感谢您的努力,但我需要有人来谈论这些问题,而不是琐碎的实施细节。
    • 对不起,我选择了单词,但唯一可以绘制窗口的地方是 onpaint,以避免闪烁覆盖 EVT_BACKGROUND 在那里什么都不做,并在 EVT_PAINT 上使用双缓冲
    • 你能举个例子,我可以复制粘贴并运行,这样我就可以看到你面临什么问题
    • 我已经粘贴了一些示例代码。输入图像应该很大,大到需要在调整大小时进行滑动缩放,以适应任何分辨率的屏幕尺寸,从足够大到可能不够小(但不会中断,因此用户可以自己做出决定)。顺便问一下什么是双缓冲?
    • 双缓冲:首先绘制到内存缓冲区,然后将其传送到目的地,这样用户就不会看到闪烁等wiki.wxpython.org/index.cgi/DoubleBufferedDrawing
    猜你喜欢
    • 1970-01-01
    • 2013-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多