views:

1491

answers:

1

My previous attempt at asking this question was horrible and I had also made some progress, please bear with me, I did not intend to re-ask this so many times, and it is not my style.

Here is the final version: I am resizing a window that contains a DC Client painted bitmap and on the EVT_SIZE event, I am resizing it by re-scaling it (using scale, not rescale) and re-painting the image. The problem is it does not appear as though its respecting the aspect ratio even though I am calculating w/h for it. Also when it grows in height the image is distorted. Finally, when another window passes over it, the image goes white. Any ideas how to fix any of these issues? My window/image class is below:

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

Also, I am having trouble understanding how to properly use the Client DC. I want to refresh the window area before re-drawing the next image, because if i dont i get weird risiduals and it looks bad. In order to fix this I tried using dc.Clear which cleans the background off. However, doing so on every size call as i would need to causes the image to flash white a million times while im re-sizing. how can i avoid this?

EDIT -

In response to Umyal's comment response - here is a very simplified version of my application. Either way I class my window generator for the images the size handler re-scaling the images causes the image to flicker badly, creating an unappealing artifact. Also, when another frame passes over the application, the image display becomes white, as if been erased.

I was thinking as a way around this - I could implement the solution windows image viewer seems to have, which is the image is only rescaled and repainted when the user lets go of the edge of the frame when resizing it. Problem with that solution is that there is no clear way to detect when the user stops resizing the frame. (wxEVT_SIZE, wxEVT_SIZING)

Here is the simplified application code, you will need to find your own images and the bigger the better. The original image dimentions are 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()
A: 

Do not keep a reference to client DC in your window instance(http://docs.wxwidgets.org/2.6/wx_wxclientdc.html) ,neither it is the proper way of drawing over window dc

instead bind to PAINT_EVENT and draw there, below i have shown the things you said add to your class

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()
Anurag Uniyal
This is bad advice for a couple reasons. First, my way of doing it could not have been 'totally wrong' because after some testing, I found that it actually performed much better than the code you provided. Second, it does not address the issues I mention, namely, that the size event and repaint causes the image to flicker constantly, an unappealing side affect. Also, the image still goes white after another frame passes over it. Why?Thanks for the effort, but I need someone who will speak to the issues, and not a petty implementation detail.
Matt1776
Sorry for my choice of words, but only place to paint window is onpaint, to avoid flicker override EVT_BACKGROUND do nothing there, and use double buffering on EVT_PAINT
Anurag Uniyal
and can you give a example which I can just copy-paste and run, so that I can see what problem you are facing
Anurag Uniyal
I've pasted some sample code. The input images are supposed to be large, large enough to require a sliding scale on resize, to fit any resolution screen size from adequately large to perhaps insufficiently small (but doesnt break so the user can make that determination themselves).By the way what is a double buffer?
Matt1776
double buffering: first draw to a memory buffer then blit it to destination so that user doesn't see flicker etc http://wiki.wxpython.org/index.cgi/DoubleBufferedDrawing
Anurag Uniyal
I run the code and i see no problem with my solution, only thing is on resize it doesn't paint so you need to call self.Refresh()(see my code) also while loading WxImage no need os passing wx.BITMAP_TYPE_JPEG, it will guess automatically and you can use any image that way
Anurag Uniyal
Great, thank you for the assistance!
Matt1776