2010-01-12 21 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