2010-01-12 19 views
1

每当我通过将EVT_PAINT事件绑定到函数来处理某个问题时,会打开框架/窗口,但当我单击右上角的关闭选项时它不能关闭框架。 任何人都可以告诉我这是什么原因?除此之外,还有很多闪烁发生。在wxPython中处理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事件,然后调整大小?在那种情况下,当我需要时,如何在中间调用绘画事件...

代码如下。

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

请张贴一些示例代码 - 这不是标准行为(我使用EVT_PAINT而没有在我的程序中出现问题) – 2010-01-13 23:05:45

+0

对不起。我已经发布了代码。谢谢 – Damodar 2010-01-14 23:02:57

回答

0

我有挂钩EVT_CLOSE时发生,我不得不打电话event.Skip(1)让它正常地处理该事件。也许类似的副作用正在进行绘画事件。

1

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

Damodar

4

只是为了任何人在将来的答案看着这个至今没有明确解释,这个问题是由于对窗口没有创造wxPaintDC你处理EVT_PAINT为参考。这个必须在当前的wx版本中完成,以避免在Windows下积累无尽的WM_PAINT消息流。

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

+0

我当然希望这是有记载的地方!刚刚受到这个bug的影响,很烦人。 – Claudiu 2013-04-17 18:32:23