本文介绍了在 wxPython 中处理 EVT_PAINT 事件的副作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每当我通过将 EVT_PAINT 事件绑定到一个函数来处理它时,就会出现一些问题,框架/窗口会打开,但当我单击框架右上角的关闭选项时,它无法关闭.谁能告诉我这可能是什么原因???除了发生大量闪烁的形式.

Whenever I handle EVT_PAINT event by binding it to a function some problem occures the frame/window opens up but it cannot be closed when I click on the close option on the top-right corner of the frame.Can any one tell me what can be the reason for this ??? Apart form that there is a lot of flicker that occures.

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

The purpose of the code is somewhat like this. I have a Main Frame class which holds a panel. In that panel I have a wxPython Notebook. There are two pages in the notebook. Each page has same structure having two panels, image panel and control panel. Image panel displays image and control panel has a file open button that opens the file to be displayed in the image panel. By default image panel shows a image called "default.png".

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

Just create some png image in the same folder as that of the program and name it as default.png so that the program works.

这是示例代码.

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()

我找到了问题发生的原因.我在错误的地方绑定了绘画事件,即在外框架中.现在,如果我在图像显示的内部图像面板中绑定绘画事件,它可以正常工作.还是谢谢...


I found why the problem was occuring. I was binding to paint event at wrong place i.e. in the outer frame. Now if I do the binding of the paint event in the inner image panel where the image is dispalyed it works fine. Thanks anyways...

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

But it seems like I am facing another problem. When I click the button "Browse" in my control panel and select a different image file to be displayed, the paint event is not called after this action. So the old image stays on the screen. But when I resize the window it paint event is called and new image is displayed.

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

Why is this happening ??? Is it because the paint event is called only for the first time and then after resizing?? In that case how can I call the paint event in the middle when I need...

代码如下.

达莫达尔

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()

推荐答案

仅供将来查看此问题的任何人参考,因为到目前为止的答案尚不清楚说明问题是由于未创建 wxPaintDC 用于处理 EVT_PAINT 的窗口.这必须在当前的 wx 版本中完成,以避免在 Windows 下堆积无休止的 WM_PAINT 消息.

Just for the reference of anybody looking at this in the future as the answers so far don't clear explain that the problem is due to not creating wxPaintDC for the window you handle EVT_PAINT for. This must be done in current wx versions to avoid accumulation of an endless stream of WM_PAINT messages under Windows.

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

FWIW the upcoming 2.9.1 release will handle this more gracefully and just warn you, via a debug message, about the problem in your code but will still validate the window to prevent the system from continuing to send you more WM_PAINTs.

这篇关于在 wxPython 中处理 EVT_PAINT 事件的副作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 20:13