在wxPython中处理EVT_PAINT事件的副作用

时间:2010-01-12 23:32:31

标签: wxpython wxwidgets

每当我通过将EVT_PAINT事件绑定到函数来处理EVT_PAINT事件时,会出现一些问题,框架/窗口会打开但是当我单击框架右上角的关闭选项时它无法关闭。 任何人都可以告诉我这可能是什么原因???除此之外,还有很多闪烁现象。

代码的目的有点像这样。我有一个主框架类,它包含一个面板。在那个面板中,我有一个wxPython Notebook。笔记本中有两页。每个页面具有相同的结构,具有两个面板,图像面板和控制面板。图像面板显示图像,控制面板有一个文件打开按钮,用于打开要在图像面板中显示的文件。默认情况下,图像面板显示名为“default.png”的图像。

只需在与程序相同的文件夹中创建一些png图像,并将其命名为default.png,以便程序正常工作。

以下是示例代码。

import os
import wx

#===========================================================================
# This is how you pre-establish a file filter so that the dialog
# only shows the extension(s) you want it to.
wildcard = "Python source (*.py)|*.py|"     \
           "Compiled Python (*.pyc)|*.pyc|" \
           "SPAM files (*.spam)|*.spam|"    \
           "Egg file (*.egg)|*.egg|"        \
           "All files (*.*)|*.*"
#===========================================================================

#========================================================================
# Set to 1 when new image is uploaded.
imageChangeFlag = [0];
#========================================================================

#========================================================================
# Set to 1 when new image is uploaded.
pageChangeFlag = [0];
#========================================================================

 # Some classes to use for the notebook pages.  Obviously you would
# want to use something more meaningful for your application, these
# are just for illustration.
class ImagePanel(wx.Panel):
    '''
        Create an image panel.
        Sets all the controls of image panel
    '''
    def __init__(self,parent):
        wx.Panel.__init__(self,parent,id=-1,
                          name="Image Panel",
                          style=wx.BORDER_THEME)
        self.SetBackgroundColour(wx.WHITE)

class ControlPanel(wx.Panel):
    '''
        Create a control panel.
        Sets all the controls of the control panel
    '''
    def __init__(self,parent):
        wx.Panel.__init__(self,parent,id=-1,
                          name="Control Panel",
                          style=wx.BORDER_THEME)
        self.SetBackgroundColour(wx.Colour(235,234,211))    

class PageOne(wx.Panel):
    '''
        This panel is the first page of the notebook and sets all the widgets in the first page.
    '''
    def __init__(self, parent, id):
        '''
            Constructor for page 1 initializes the first page of the notebook
        '''
        wx.Panel.__init__(self, parent, id, name="Network Visualization")        
        #t = wx.StaticText(self, -1, "This is a PageOne object", (20,20))

        self.path = "default.png"

        #====================================================================
        # Set the Network Visualization Page.
        #====================================================================
        self.imagePanel = ImagePanel(self)
        self.controlPanel = ControlPanel(self)

        fileOpenButton = wx.Button(self.controlPanel, -1, "Browse", (30,30))
        self.Bind(wx.EVT_BUTTON, self.onFileOpen, fileOpenButton)

        controlSizer = wx.BoxSizer()
        controlSizer.Add(fileOpenButton,1)
        self.controlPanel.SetSizer(controlSizer)

        box = wx.BoxSizer()
        box.Add(self.imagePanel,3,wx.EXPAND)
        box.Add(self.controlPanel,1,wx.EXPAND)
        self.SetSizer(box)

        self.loadImage(self.path)

    def onFileOpen(self,event):
        fileOpenDlg = wx.FileDialog(self.controlPanel, 
                                    message="Select a file",
                                    defaultDir=os.getcwd(), 
                                    defaultFile="",
                                    wildcard=wildcard,
                                    style=wx.OPEN |wx.CHANGE_DIR)

        # Show the dialog and retrieve the user response. If it is the OK response, 
        # process the data.
        if fileOpenDlg.ShowModal() == wx.ID_OK:
            image = fileOpenDlg.GetPath()
            # Load the new image and set the imageChangeFlag to 1. 
            self.loadImage(image)
            imageChangeFlag[0] = 1;


        # Destroy the dialog. Don't do this until you are done with it!
        # BAD things can happen otherwise!
        fileOpenDlg.Destroy()     

    def loadImage(self,image):
        '''
            Initializes the image.
            Finds the properties of the image like the aspect ratio and bitmap. 
        '''
        self.png = wx.Image(image, wx.BITMAP_TYPE_ANY)
        imageHeight = self.png.GetHeight()
        imageWidth = self.png.GetWidth()
        self.aspectRatio = imageWidth/imageHeight
        self.bitmap = wx.BitmapFromImage(self.png)

    def getImagePanel(self):
        return self.imagePanel

    def getControlPanel(self):
        return self.controlPanel

    def getImage(self):
        return self.png

    def getImageBitmap(self):
        return self.bitmap

    def getImageAspectRatio(self):
        return self.aspectRatio


class PageTwo(wx.Panel):
    def __init__(self, parent, id):
        '''
            Constructor for page 1 initializes the first page of the notebook
        '''
        wx.Panel.__init__(self, parent, id, name="Graph Visualization")
        #t = wx.StaticText(self, -1, "This is a PageTwo object", (40,40))

        self.path = "default.png"

        #====================================================================
        # Set the Network Visualization Page.
        #====================================================================
        self.imagePanel = ImagePanel(self)
        self.controlPanel = ControlPanel(self)

        fileOpenButton = wx.Button(self.controlPanel, -1, "Browse", (30,30))
        self.Bind(wx.EVT_BUTTON, self.onFileOpen, fileOpenButton)

        controlSizer = wx.BoxSizer()
        controlSizer.Add(fileOpenButton,1)
        self.controlPanel.SetSizer(controlSizer)        

        box = wx.BoxSizer()
        box.Add(self.imagePanel,3,wx.EXPAND)
        box.Add(self.controlPanel,1,wx.EXPAND)
        self.SetSizer(box)

        self.loadImage(self.path)

    def onFileOpen(self,event):
        fileOpenDlg = wx.FileDialog(self.controlPanel, 
                                    message="Select a file",
                                    defaultDir=os.getcwd(), 
                                    defaultFile="",
                                    wildcard=wildcard,
                                    style=wx.OPEN |wx.CHANGE_DIR)

        # Show the dialog and retrieve the user response. If it is the OK response, 
        # process the data.
        if fileOpenDlg.ShowModal() == wx.ID_OK:
            image = fileOpenDlg.GetPath()
            # Load the new image and set the imageChangeFlag to 1.
            self.loadImage(image)
            imageChangeFlag[0] = 1;

        # Destroy the dialog. Don't do this until you are done with it!
        # BAD things can happen otherwise!
        fileOpenDlg.Destroy()

    def loadImage(self,image):
        '''
            Initializes the image.
            Finds the properties of the image like the aspect ratio and bitmap. 
        '''
        self.png = wx.Image(image, wx.BITMAP_TYPE_ANY)
        imageHeight = self.png.GetHeight()
        imageWidth = self.png.GetWidth()
        self.aspectRatio = imageWidth/imageHeight
        self.bitmap = wx.BitmapFromImage(self.png)

    def getImagePanel(self):
        return self.imagePanel

    def getControlPanel(self):
        return self.controlPanel

    def getImage(self):
        return self.png

    def getImageBitmap(self):
        return self.bitmap

    def getImageAspectRatio(self):
        return self.aspectRatio


class Notebook(wx.Notebook):
    '''
        Creates a Notebook.
    '''
    def __init__(self,parent):
        wx.Notebook.__init__(self,parent,size=parent.GetSizeTuple())

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, 
                          title="Social Network Analysis",
                          size=(900,700))

        #======================================================================
        # Create a panel and a notebook on the panel
        #======================================================================
        self.p = wx.Panel(self,size=self.GetSizeTuple())     
        self.nb = Notebook(self.p)

        #====================================================================
        # Create the page windows as children of the notebook
        #====================================================================
        self.networkVisualizationPage = PageOne(self.nb,id=1)
        self.graphVisualizationPage = PageTwo(self.nb,id=2)

        #=======================================================================================
        # Initialize the page id to 1.
        # By default Network Visualization Page will be selected first.
        # Get the image panel from networkVisualization page.
        # Then get the image to be displayed on the image panel of networkVisualization page.        
        #=======================================================================================
        self.pageId = 1
        self.pageSelect()    
        self.imageRefresh()

        #======================================================================
        # Add the pages to the notebook with the label to show on the tab
        #======================================================================
        self.nb.AddPage(self.networkVisualizationPage, "Network Visualization")
        self.nb.AddPage(self.graphVisualizationPage, "Graph Visualization")

        #======================================================================
        # Finally, put the notebook in a sizer for the panel to manage the layout
        #======================================================================
        sizer = wx.BoxSizer()
        sizer.Add(self.nb, 1, wx.EXPAND)
        self.p.SetSizer(sizer)        

        self.Bind(wx.EVT_PAINT, self.onPaint)
        self.Bind(wx.EVT_SIZE, self.onResize)
        self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onPageChange)
        self.Bind(wx.EVT_ERASE_BACKGROUND , self.erase)

    def erase(self,event):
        pass

    def onPageChange(self,event):
        '''
            Handles the EVT_NOTEBOOK_PAGE_CHANGED event
        '''
        pageChangeFlag[0] = 1
        self.pageId = self.nb.GetCurrentPage().GetId()
        self.pageSelect()
        self.imageRefresh()
        #print "Page Change"
        #print self.pageId

    def onPaint(self,event):
        '''
            Handles EVT_PAINT event.
        '''
        if(imageChangeFlag[0] == 1 or pageChangeFlag[0] == 1):
            imageChangeFlag[0] = 0
            pageChangeFlag[0] = 0
            self.imageRefresh()
            (w, h) = self.getBestSize()
            self.scaledPNG = self.png.Scale(w,h,quality=wx.IMAGE_QUALITY_HIGH)
            self.bitmap = wx.BitmapFromImage(self.scaledPNG)
            self.Refresh()
        imagePanelDC = wx.PaintDC(self.imagePanel)
        imagePanelDC.DrawBitmap(self.bitmap, 0, 0, useMask=False)
        #controlPanelDC = wx.PaintDC(self.controlPanel)
        imagePanelDC.Destroy()

    def onResize(self,event):
        '''
            Handles EVT_SIZE event.
        '''    
        self.p.SetSize(self.GetSizeTuple())
        (w, h) = self.getBestSize()
        self.imageRefresh()
        self.scaledPNG = self.png.Scale(w,h,quality=wx.IMAGE_QUALITY_HIGH)
        self.bitmap = wx.BitmapFromImage(self.scaledPNG)
        self.Refresh()

    def imageRefresh(self):
        #======================================================================
        # Initialize the image parameters
        #======================================================================
        if(self.pageId == 1):            
            self.png = self.networkVisualizationPage.getImage()
            self.bitmap = self.networkVisualizationPage.getImageBitmap()
            self.aspectRatio = self.networkVisualizationPage.getImageAspectRatio()
        elif(self.pageId == 2):
            self.png = self.graphVisualizationPage.getImage()
            self.bitmap = self.graphVisualizationPage.getImageBitmap()
            self.aspectRatio = self.graphVisualizationPage.getImageAspectRatio()

    def pageSelect(self):
        #========================================================================
        # Selects the image panel and control panel of appropriate page
        #========================================================================
        if(self.pageId == 1):
            self.imagePanel = self.networkVisualizationPage.getImagePanel()
            self.controlPanel = self.networkVisualizationPage.getControlPanel()
        elif(self.pageId == 2):
            self.imagePanel = self.graphVisualizationPage.getImagePanel()
            self.controlPanel = self.graphVisualizationPage.getControlPanel()

    def getBestSize(self):
        '''
            Returns the best size the image can have based on the aspect ratio of the image.
        '''
        (w,h) = self.imagePanel.GetSizeTuple()
        #print "Image Panel Size = "
        #print (w,h)
        reductionFactor = 0.1
        # Reduce the height by 20 units and change width of the image according to aspect ratio
        newHeight = int(h - (h * reductionFactor))
        newWidth = int (self.aspectRatio * newHeight)
        newSize = (newWidth,newHeight)
        #print "Image Size = "
        #print newSize
        return newSize

if __name__ == "__main__":
    app = wx.App()
    MainFrame().Show()
    app.MainLoop()

我发现问题出现的原因。我在错误的地方,即在外框架中绑定油漆事件。现在,如果我在显示图像的内部图像面板中绑定绘制事件,它可以正常工作。不管怎样,谢谢......

但似乎我正面临另一个问题。当我单击控制面板中的“浏览”按钮并选择要显示的其他图像文件时,在此操作后不会调用paint事件。因此旧图像保留在屏幕上。但是当我调整窗口大小时,它会调用绘制事件并显示新图像。

为什么会这样?是因为paint事件只是第一次调用然后调整大小后?在那种情况下,当我需要时,如何在中间调用paint事件...

代码如下。

Damodar

import wx
import os

#===========================================================================
# This is how you pre-establish a file filter so that the dialog
# only shows the extension(s) you want it to.
wildcard = "Python source (*.py)|*.py|"     \
           "Compiled Python (*.pyc)|*.pyc|" \
           "SPAM files (*.spam)|*.spam|"    \
           "Egg file (*.egg)|*.egg|"        \
           "All files (*.*)|*.*"
#===========================================================================

#========================================================================
# Set to 1 when new image is uploaded.
imageChangeFlag = [0];
#========================================================================

#========================================================================
# Set to 1 when page is changed.
pageChangeFlag = [0];
#========================================================================

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None,
                          title="Social Network Analysis",
                          size=(400,400),
                          style=wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE)

class MainPanel(wx.Panel):
    def __init__(self,parent=None):
        wx.Panel.__init__(self,parent,size=parent.GetSizeTuple())

        #==========================================================
        # Parent frame of the main panel
        #==========================================================
        self.parent = parent        

        #==========================================================
        # Display the .png image in the panel
        #==========================================================
        #image = "default.png"
        #self.loadImage(image)

class Notebook(wx.Notebook):
    '''
        Creates a Notebook.
    '''
    def __init__(self,parent):
        wx.Notebook.__init__(self,parent,size=parent.GetSizeTuple())


class NewPage(wx.Panel):
    '''
        This panel is the first page of the notebook and sets all the widgets in the first page.
    '''
    def __init__(self, parent, parentPanel, parentFrame, id, title):
        '''
            Constructor for page 1 initializes the first page of the notebook
        '''
        wx.Panel.__init__(self, parent, id, name=title)        

        #====================================================================
        # Set the Network Visualization Page.
        #====================================================================
        self.imagePanel = ImagePanel(self,parent,parentPanel,parentFrame)
        self.controlPanel = ControlPanel(self,self.imagePanel)

        box = wx.BoxSizer()
        box.Add(self.imagePanel,3,wx.EXPAND)
        box.Add(self.controlPanel,1,wx.EXPAND)
        self.SetSizer(box)

class ImagePanel(wx.Panel):
    '''
        Create an image panel.
        Sets all the controls of image panel
    '''
    def __init__(self,parent=None,parentBook=None,parentPanel=None,parentFrame=None):
        wx.Panel.__init__(self,parent,id=-1,
                          name="Image Panel",
                          style=wx.BORDER_THEME)
        self.SetBackgroundColour(wx.WHITE)

        self.parent = parent
        self.parentBook = parentBook
        self.parentPanel = parentPanel
        self.parentFrame = parentFrame        

        self.path = "default.png"
        self.loadImage(self.path)
        self.Bind(wx.EVT_PAINT, self.onPaint)
        self.Bind(wx.EVT_SIZE, self.onResize)

    def loadImage(self,image):
        '''
            Initializes the image.
            Finds the properties of the image like the aspect ratio and bitmap. 
        '''
        self.png = wx.Image(image, wx.BITMAP_TYPE_ANY)
        imageHeight = self.png.GetHeight()
        imageWidth = self.png.GetWidth()
        self.aspectRatio = imageWidth/imageHeight
        self.bitmap = wx.BitmapFromImage(self.png)

    def onPaint(self,event):
        '''
            Handles EVT_PAINT event.
        '''
        if(imageChangeFlag[0] == 1):
            imageChangeFlag[0] = 0
            (w, h) = self.getBestSize()
            self.scaledPNG = self.png.Scale(w,h,quality=wx.IMAGE_QUALITY_HIGH)
            self.bitmap = wx.BitmapFromImage(self.scaledPNG)
            self.Refresh()       
        imagePanelDC = wx.PaintDC(self)
        imagePanelDC.DrawBitmap(self.bitmap, 0, 0, useMask=False)
        #imagePanelDC.Destroy()

    def onResize(self,event):
        '''
            Handles EVT_SIZE event.
        '''  
        self.parentPanel.SetSize(self.parentFrame.GetSizeTuple())
        (w, h) = self.getBestSize()
        self.scaledPNG = self.png.Scale(w,h,quality=wx.IMAGE_QUALITY_HIGH)
        self.bitmap = wx.BitmapFromImage(self.scaledPNG)
        self.Refresh()       

    def getBestSize(self):
        '''
            Returns the best size the image can have based on the aspect ratio of the image.
        '''
        (w,h) = self.GetSizeTuple()
        #print "Image Panel Size = "
        #print (w,h)
        reductionFactor = 0.1
        # Reduce the height by 20 units and change width of the image according to aspect ratio
        newHeight = int(h - (h * reductionFactor))
        newWidth = int (self.aspectRatio * newHeight)
        newSize = (newWidth,newHeight)
        #print "Image Size = "
        #print newSize
        return newSize

class ControlPanel(wx.Panel):
    '''
        Create a control panel.
        Sets all the controls of the control panel
    '''
    def __init__(self,parent,imagePanel):
        wx.Panel.__init__(self,parent,id=-1,
                          name="Control Panel",
                          style=wx.BORDER_THEME)
        self.SetBackgroundColour(wx.Colour(235,234,211))
        self.imagePanel = imagePanel
        fileOpenButton = wx.Button(self, -1, "Browse", (30,30))
        self.Bind(wx.EVT_BUTTON, self.onFileOpen, fileOpenButton)

        controlSizer = wx.BoxSizer()
        controlSizer.Add(fileOpenButton,1)
        self.SetSizer(controlSizer)

    def onFileOpen(self,event):
        fileOpenDlg = wx.FileDialog(self, 
                                    message="Select a file",
                                    defaultDir=os.getcwd(), 
                                    defaultFile="",
                                    wildcard=wildcard,
                                    style=wx.OPEN |wx.CHANGE_DIR)

        # Show the dialog and retrieve the user response. If it is the OK response, 
        # process the data.
        if fileOpenDlg.ShowModal() == wx.ID_OK:
            image = fileOpenDlg.GetPath()
            # Load the new image and set the imageChangeFlag to 1.
            self.imagePanel.loadImage(image)
            imageChangeFlag[0] = 1;

        # Destroy the dialog. Don't do this until you are done with it!
        # BAD things can happen otherwise!
        fileOpenDlg.Destroy()     


app = wx.PySimpleApp()
# Create Main Frame
frame = MainFrame()

# Create Main Panel inside Main Frame
panel = MainPanel(frame)

# Create a notebook inside the Main Panel
nb = Notebook(panel)

# Create the page windows as children of the notebook
networkVisualizationPage = NewPage(nb,panel,frame,id=1,title="Network Visualization")
graphVisualizationPage = NewPage(nb,panel,frame,id=2,title="Graph Visualization")

# Add the pages to the notebook with the label to show on the tab
nb.AddPage(networkVisualizationPage, "Network Visualization")
nb.AddPage(graphVisualizationPage, "Graph Visualization")

# Finally, put the notebook in a sizer for the panel to manage the layout
sizer = wx.BoxSizer()
sizer.Add(nb, 1, wx.EXPAND)
panel.SetSizer(sizer)   

frame.Show(1)
app.MainLoop()    

3 个答案:

答案 0 :(得分:4)

仅供将来查看此内容的人参考,因为到目前为止的答案尚未明确解释该问题是由于没有为您处理wxPaintDC的窗口创建EVT_PAINT。这个必须在当前的wx版本中完成,以避免在Windows下累积无休止的WM_PAINT消息。

FWIW即将推出的2.9.1版本将更优雅地处理这个问题,并通过调试消息警告您代码中的问题,但仍会验证窗口以防止系统继续向您发送更多{{1 }}第

答案 1 :(得分:1)

我也找到了答案。我不得不刷新应该显示图像的面板。 self.imagePanel.refresh()将调用paint事件。

Damodar

答案 2 :(得分:0)

我在挂钩EVT_CLOSE时已经发生了这种情况,我不得不调用event.Skip(1)让它正常处理事件。也许油漆事件会产生类似的副作用。