wxPython 畫圖板


  終於開始Python學習之旅了,姑且以一個“畫圖板”小項目開始吧。放慢腳步,一點一點地學習。

 

1月28日更新  

  第一次遇到的麻煩便是“重繪”,查了好多資料,終於重繪成功了。

#-*- encoding: gbk -*
import wx

""" this version use class 'PaintDC' and 'ClientDC' """

class MainFrame(wx.Frame):
    
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "畫圖板 - 芒果布丁", (0, 0), (800, 500))
        self.Center(wx.BOTH) # 窗口居中顯示
        self.x1, self.x2, self.y1, self.y2 = 0, 0, 0, 0
        self.st = 'line' 
        self.pos = (0,0)
        self.pen = wx.Pen("green", 1, wx.SOLID)
        self.brush = wx.Brush('', wx.TRANSPARENT)  #透明填充
        self.shapes = []
                          
        self.SetBackgroundColour("black")
        self.b1 = wx.Button(self, -1, label="矩形", pos=(10, 10), size=(50, 30))
        self.b2 = wx.Button(self, -1, label="圓形", pos=(10, 50), size=(50, 30))
        self.b3 = wx.Button(self, -1, label="直線", pos=(10, 90), size=(50, 30))
        
        self.b1.SetDefault()  
        
        self.InitBuffer()
        
        self.Bind(wx.EVT_BUTTON, self.ToRect, self.b1)
        self.Bind(wx.EVT_BUTTON, self.ToOval, self.b2)
        self.Bind(wx.EVT_BUTTON, self.ToLine, self.b3)
        
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        
        
    def InitBuffer(self):
        size = self.GetClientSize()
        self.buffer = wx.EmptyBitmap(size.width, size.height)
        dc = wx.BufferedDC(None, self.buffer)
        dc.SetPen(self.pen)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.SetBrush(self.brush)
        dc.Clear()
        self.Draw(dc)
        
    def ToRect(self, event):
        self.st = 'rect'
        
    def ToOval(self, event):
        self.st = 'oval'
        
    def ToLine(self, event):
        self.st = 'line'
        
    def OnLeftDown(self, event):
        
        self.p1 = event.GetPositionTuple()
        self.x1, self.y1 = self.p1
        
    def OnLeftUp(self, event):
        self.p2 = event.GetPositionTuple()
        self.shapes.append((self.st, self.p1 + self.p2))
        dc = wx.ClientDC(self)
        self.Draw(dc)
        
    def OnPaint(self, event):
        dc = wx.PaintDC(self) # 處理一個paint(描繪)請求
        self.Draw(dc)
    
    def Draw(self, dc):
        dc.SetPen(self.pen)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.SetBrush(self.brush)
        dc.Clear()
        for st,(x1,y1,x2,y2) in self.shapes:
            
            if st == 'line':
                dc.DrawLine(x1, y1, x2, y2)
            elif st == 'oval':
                dc.DrawEllipse(x1, y1, x2-x1, y2-y1)
            elif st == 'rect':
                dc.DrawRectangle(x1, y1, x2-x1, y2-y1)   
        
        
if __name__ =='__main__':
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
    
畫圖板 version1
#-*- encoding: gbk -*
import wx

""" this version use class 'BufferedPaintDC' and 'BufferedDC' """

class MainFrame(wx.Frame):
    
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "畫圖板 - 芒果布丁", (0, 0), (800, 500))
        self.Center(wx.BOTH) # 窗口居中顯示
        self.x1, self.x2, self.y1, self.y2 = 0, 0, 0, 0
        self.st = 'line' 
        self.pos = (0,0)
        self.pen = wx.Pen("green", 1, wx.SOLID)
        self.brush = wx.Brush('', wx.TRANSPARENT)  #透明填充
        self.shapes = []
        
        self.InitBuffer()
                          
        self.SetBackgroundColour("black")
        self.b1 = wx.Button(self, -1, label="矩形", pos=(10, 10), size=(50, 30))
        self.b2 = wx.Button(self, -1, label="圓形", pos=(10, 50), size=(50, 30))
        self.b3 = wx.Button(self, -1, label="直線", pos=(10, 90), size=(50, 30))
        
        self.Bind(wx.EVT_BUTTON, self.ToRect, self.b1)
        self.Bind(wx.EVT_BUTTON, self.ToOval, self.b2)
        self.Bind(wx.EVT_BUTTON, self.ToLine, self.b3)
        
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def InitBuffer(self):
        size = self.GetClientSize()
        self.buffer = wx.EmptyBitmap(size.width, size.height)
        
    def ToRect(self, event):
        self.st = 'rect'
        
    def ToOval(self, event):
        self.st = 'oval'
        
    def ToLine(self, event):
        self.st = 'line'
        
    def OnLeftDown(self, event):
        self.p1 = event.GetPositionTuple()
        self.x1, self.y1 = self.p1
        
    def OnLeftUp(self, event):
        self.p2 = event.GetPositionTuple()
        self.shapes.append((self.st, self.p1 + self.p2))
        self.Draw()
        
    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self.buffer) # 處理一個paint(描繪)請求
    
    def Draw(self):
        dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
        dc.SetPen(self.pen)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.SetBrush(self.brush)
        dc.Clear()
        for st,(x1,y1,x2,y2) in self.shapes:
            if st == 'line':
                dc.DrawLine(x1, y1, x2, y2)
            elif st == 'oval':
                dc.DrawEllipse(x1, y1, x2-x1, y2-y1)
            elif st == 'rect':
                dc.DrawRectangle(x1, y1, x2-x1, y2-y1)   
        
        
if __name__ =='__main__':
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
    
畫圖板 version2

  以上兩種方法差別不大,第二種方法用buffer緩沖,據說可以消除畫布閃爍,但第一種方法在沒有緩沖的情況下也沒有出現閃爍的情況。但是有必要對幾個DC進行詳細說明一下:

 

如何在屏幕上繪畫

要在屏幕上繪畫,我們要用到一個名為device context(設備上下文)的wxPython對象。設備上下文代表抽象的設備,它對於所有的設備有一套公用的繪畫方法,所以對於不同的設備,你的代碼是相同的,而不用考慮你所在的具體設備。設備上下文使用抽象的wxPython的類wx.DC和其子類來代表。由於wx.DC是抽象的,所以對於你的應用程序,你需要使用它的子類。

使用設備上下文

表6.1顯示了wx.DC的子類及其用法。設備上下文用來在wxPython窗口部件上繪畫,它應該是局部的,臨時性的,不應該以實例變量、全局變量或其它形式在方法調用之間保留。在某些平台上,設備上下文是有限的資源,長期持有wx.DC可能導致你的程序不穩定。由於wxPython內部使用設備上下文的方式,對於在窗口部件中繪畫,就存在幾個有着細微差別的wx.DC的子類。第十二章將更詳細地說明這些差別。

 

表6.1

wx.BufferedDC:用於緩存一套繪畫命令,直到命令完整並准備在屏幕上繪畫。這防止了顯示中不必要的閃爍。

wx.BufferedPaintDC:和wx.BufferedDC一樣,但是只能用在一個wx.PaintEvent的處理中。僅臨時創建該類的實例。

wx.ClientDC:用於在一個窗口對象上繪畫。當你想在窗口部件的主區域上(不包括邊框或別的裝飾)繪畫時使用它。主區域有時也稱為客戶區。wx.ClientDC類也應臨時創建。該類僅適用於wx.PaintEvent的處理之外。

wx.MemoryDC:用於繪制圖形到內存中的一個位圖中,此時不被顯示。然后你可以選擇該位圖,並使用wx.DC.Blit()方法來把這個位圖繪畫到一個窗口中。

wx.MetafileDC:在Windows操作系統上,wx.MetafileDC使你能夠去創建標准窗口圖元文件數據。

wx.PaintDC:等同於wx.ClientDC,除了它僅用於一個wx.PaintEvent的處理中。僅臨時創建該類的實例。

wx.PostScriptDC:用於寫壓縮的PostScript文件。

wx.PrinterDC:用於Windows操作系統上,寫到打印機。

wx.ScreenDC:用於直接在屏幕上繪畫,在任何被顯示的窗口的頂部或外部。該類只應該被臨時創建。

wx.WindowDC:用於在一個窗口對象的整個區域上繪畫,包括邊框以及那些沒有被包括在客戶區域中的裝飾。非Windows系統可能不支持該類。

                                                 ——引自這個網站

 

  屏幕設備上下文是指臨時創建的。這意味你無論你何時需要它們時只應該局部地創建它們,並且使它們能夠被正常垃圾回收。你不應該試圖以一個實例變量的形式占有一個設備上下文——這是不安全的,並可能導致程序的不穩定。

  通常,你會使用wx.ClientDC或wx.PaintDC來繪制到屏幕上的一個窗口。使用哪個取決於你執行繪制的時機。如果你在一個EVT_PAINT事件處理期間繪制到屏幕,那么你必須使用wx.PaintDC。在其它的時間,你必須使用wx.ClientDC。實際上,無論你何時綁定一個處理器到EVT_PAINT事件,你都必須在該處理器方法中創建一個wx.PaintDC對象,即使你不使用它(不創建一個wx.PaintDC會導致平台認為該事件並未完全處理,並因此會發送另一個事件)。描繪(paint)事件要求不同的設備上下文的原因是,這個wx.PaintDC實例是被優化來只在重繪事件期間的窗口刷新區域中進行繪制的,以便重繪更快。

  屏幕設備上下文是指臨時創建的。這意味你無論你何時需要它們時只應該局部地創建它們,並且使它們能夠被正常垃圾回收。你不應該試圖以一個實例變量的形式占有一個設備上下文——這是不安全的,並可能導致程序的不穩定。

  通常,你會使用wx.ClientDC或wx.PaintDC來繪制到屏幕上的一個窗口。使用哪個取決於你執行繪制的時機。如果你在一個EVT_PAINT事件處理期間繪制到屏幕,那么你必須使用wx.PaintDC。在其它的時間,你必須使用wx.ClientDC。實際上,無論你何時綁定一個處理器到EVT_PAINT事件,你都必須在該處理器方法中創建一個wx.PaintDC對象,即使你不使用它(不創建一個wx.PaintDC會導致平台認為該事件並未完全處理,並因此會發送另一個事件)。描繪(paint)事件要求不同的設備上下文的原因是,這個wx.PaintDC實例是被優化來只在重繪事件期間的窗口刷新區域中進行繪制的,以便重繪更快。

  你可以經由一個簡單的構造函數來創建一個客戶區(client)或描繪(paint)上下文,構造函數的一個參數是你希望在其上進行繪制的wxPython窗口部件,兩者的構造函數分別是wx.ClientDC(window)和wx.PaintDC(window)。當你使用這兩個上下文時,你將只能在該窗口部件的客戶區中進行繪制。這意味在一個框架中,你不能在邊框、標題欄或其它裝飾物上進行繪制。

  如果你需要在框架的整個區域上進行繪制,包括邊框和裝飾物,那么你應該使用wx.WindowDC。創建一個wx.WindowDC的方法和wx.ClientDC相似,相應的構造函數是wx.WindowDC(window)。和wx.ClientDC一樣,你不應該在一個描繪(paint)事件期間創建一個wx.WindowDC——邊框繪制行為與描繪(paint)設備上下文的局部優化是不兼容的。

  有時,你不願被限制於只繪制到一個單一的窗口,你想讓整個屏幕作為你的畫布。在這種情況下,你可以使用wx.ScreenDC。同樣地,你不能在一描繪(paint)事件期間創建它。這個構造函數沒有參數(因為你不需要再指定繪制到的對象)——wx.ScreenDC()。創建了一個wx.ScreenDC之后,你就可以像使用其它的設備上下文一樣使用它了。你繪制的圖像將顯示在你的顯示器中的所有的窗口的上層。

  緩沖使你能夠發送單獨的繪制命令到緩沖區,然后一次性地把它們繪制到屏幕。當你一下做幾個重繪時,這防止了屏幕的閃爍。因此,當做動畫或其它屏幕密集繪制時,緩沖是一個常見的技術。

  在wxPython中有兩個緩沖設備上下文——wx.BufferedDC,它可被用於緩沖任何的設備上下文(但是通常只用於wx.ClientDC);wx.BufferedPaintDC,它專門被設計用來緩沖一個wx.PaintDC。作為內存設備上下文的一個簡單包裝,這兩個緩沖上下文的工作方式基本上是一樣。wxBufferedDC的構造函數要求一個設備上下文和一個可選的位圖作為參數——wx.BufferedDC(dc, buffer=None)。另一方面,wx.BufferedPaintDC要求一個窗口和一個可選的位圖作為參數——wx.BufferedPaintDC(dc, buffer=None)。參數dc是最終你想要繪制到的設備上下文,對於wx.BufferedPaintDC,窗口參數被用於內在地創建一個wx.PaintDC,buffer參數是一個位圖,它被作為臨時的緩沖。如果buffer參數沒有指定,那么該設備上下文內在地創建它自己的位圖。一旦緩沖設備上下文被創建,你就可以像其它的設備上下文樣使用它。在內部,緩沖上下文使用一個內存設備上下文和該位圖來儲存繪制。這樣的捷徑就是你不需要做任何特別的事來得到緩沖以繪制到實際的設備上下文。當緩沖設備上下文被垃圾回收時(通常當該方法結束且它已經退出作用域時),C++銷毀函數觸發Blit(),這將繪制緩沖區的內容到實際的設備上下文,而沒有更多的工作需要你做

 

                                                 ——引自這個網址

  用緩沖的方式的重繪部分為什么OnPaint方法只是定義一個BufferedPaintDC即可?

  對於所有的顯示要求,都將產生wx.EVT_PAINT事件(描繪事件),並調用我們這里的方法OnPaint進行屏幕刷新(重繪),你可以看到這是出乎意料的簡單:創建一個緩存的畫圖設備上下文。實際上wx.PaintDC被創建(因為我們處在一個Paint請求里,所以我們需要wx.PaintDC而非一個wx.ClientDC實例),然后在dc實例被刪除后(函數返回時被銷毀),位圖被一塊塊地傳送(blit)給屏幕並最終顯示。關於緩存的更詳細的信息將在隨后的段落中提供。

                                                        

 1月31日大年初一更新

  這個版本主要更新了鼠標隨動繪畫並修正了一些bug。

#-*- encoding: gbk -*
import wx

""" this version use class 'BufferedPaintDC' and 'BufferedDC' """

class MainFrame(wx.Frame):
    
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "畫圖板 - 芒果布丁", (0, 0), (800, 500))
        self.Center(wx.BOTH) # 窗口居中顯示
        self.x1, self.x2, self.y1, self.y2 = 0, 0, 0, 0
        self.st = 'line' 
        self.pos = (0,0)
        self.pen = wx.Pen("green", 1, wx.SOLID)
        self.brush = wx.Brush('', wx.TRANSPARENT)  #透明填充
        self.shapes = []
                          
        self.SetBackgroundColour("black")
        self.b1 = wx.Button(self, -1, label="矩形", pos=(10, 10), size=(50, 30))
        self.b2 = wx.Button(self, -1, label="圓形", pos=(10, 50), size=(50, 30))
        self.b3 = wx.Button(self, -1, label="直線", pos=(10, 90), size=(50, 30))
        
        self.b1.SetDefault()  
        
        self.InitBuffer()
        
        self.Bind(wx.EVT_BUTTON, self.ToRect, self.b1)
        self.Bind(wx.EVT_BUTTON, self.ToOval, self.b2)
        self.Bind(wx.EVT_BUTTON, self.ToLine, self.b3)
        
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_MOTION, self.OnMotion)
        
        
    def InitBuffer(self):
        size = self.GetClientSize()
        self.buffer = wx.EmptyBitmap(size.width, size.height)
        dc = wx.BufferedDC(None, self.buffer)
        dc.SetPen(self.pen)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.SetBrush(self.brush)
        dc.Clear()
        self.Draw(dc)
        
    def ToRect(self, event):
        self.st = 'rect'
        
    def ToOval(self, event):
        self.st = 'oval'
        
    def ToLine(self, event):
        self.st = 'line'
        
    def OnLeftDown(self, event):
        self.p1 = event.GetPositionTuple()
        self.x1, self.y1 = self.p1
        self.CaptureMouse()#6 捕獲鼠標
        
    def OnMotion(self, event):
        if event.Dragging() and event.LeftIsDown():#8 確定是否在拖動
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)#9 創建另一個緩存的上下文
            self.drawMotion(dc, event)
        event.Skip()
    
    def drawMotion(self, dc, event):
        self.p2 = event.GetPositionTuple()
        self.shapes.append((self.st, self.p1 + self.p2))
        self.InitBuffer()
        self.shapes.pop(len(self.shapes)-1)
                      
    def OnLeftUp(self, event):
        self.p2 = event.GetPositionTuple()
        self.shapes.append((self.st, self.p1 + self.p2))
        self.InitBuffer()
        self.ReleaseMouse()#7 釋放鼠標
        
    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self.buffer) # 處理一個paint(描繪)請求
    
    def Draw(self, dc):
        for st,(x1,y1,x2,y2) in self.shapes:
            
            if st == 'line':
                dc.DrawLine(x1, y1, x2, y2)
            elif st == 'oval':
                dc.DrawEllipse(x1, y1, x2-x1, y2-y1)
            elif st == 'rect':
                dc.DrawRectangle(x1, y1, x2-x1, y2-y1)   
        #self.Refresh()  # 不能手動刷新 會閃爍
        
        
if __name__ =='__main__':
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
    
畫圖板 version3

#6:CaptureMouse()方法控制了鼠標並在窗口的內部捕獲鼠標,即使是你拖動鼠標到窗口邊框的外面,它仍然只響應窗口內的鼠標動作,也就是說當畫一個圖形並將鼠標移到了窗口外,當前圖形也有效並被畫在屏幕上。在程序的后面必須調用ReleaseMouse()來取消其對鼠標的控制。否則該窗口將無法通過鼠標關閉等,試將#7注釋掉。

 

 2月11日 更新

  這個版本添加了菜單功能、菜單事件、圖片打開功能。

#-*- encoding: gbk -*
import wx
import os

""" this version use class 'BufferedPaintDC' and 'BufferedDC' """

class MainFrame(wx.Frame):
    
    def __init__(self):
        wx.Frame.__init__(self, None, -1, "畫圖板 - 芒果布丁", (0, 0), (800, 500))
        self.Center(wx.BOTH) # 窗口居中顯示
        self.x1, self.x2, self.y1, self.y2 = 0, 0, 0, 0
        self.iscaptured = -1
        self.p1 = (0, 0)
        self.p2 = (0, 0)
        self.st = 'line'
        self.pos = (0,0)
        self.pen = wx.Pen("green", 1, wx.SOLID)
        self.img = None
        self.brush = wx.Brush('', wx.TRANSPARENT)  #透明填充
        self.shapes = []
                          
        self.SetBackgroundColour("black")
        self.b1 = wx.Button(self, -1, label="矩形", pos=(10, 10), size=(50, 30))
        self.b2 = wx.Button(self, -1, label="圓形", pos=(10, 50), size=(50, 30))
        self.b3 = wx.Button(self, -1, label="直線", pos=(10, 90), size=(50, 30))
        
        self.b1.SetDefault()  
        
        self.InitBuffer()
        
        self.Bind(wx.EVT_BUTTON, self.ToRect, self.b1)
        self.Bind(wx.EVT_BUTTON, self.ToOval, self.b2)
        self.Bind(wx.EVT_BUTTON, self.ToLine, self.b3)
        
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_MOTION, self.OnMotion)
        
        self.SetMenuBar(self.getMenu())
        
    def InitBuffer(self):
        size = self.GetClientSize()
        mm = wx.DisplaySize()  # 獲取屏幕大小
        self.buffer = wx.EmptyBitmap(mm[0], mm[1])
        dc = wx.BufferedDC(None, self.buffer)
        dc.SetPen(self.pen)
        dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
        dc.SetBrush(self.brush)
        dc.Clear()
        self.Draw(dc)
        
    def ToRect(self, event):
        self.st = 'rect'
        
    def ToOval(self, event):
        self.st = 'oval'
        
    def ToLine(self, event):
        self.st = 'line'
        
    def OnLeftDown(self, event):
        self.p1 = event.GetPositionTuple()
        self.x1, self.y1 = self.p1
        self.CaptureMouse()#6 捕獲鼠標
        self.iscaptured = 1
        
    def OnLeftUp(self, event):
        if self.iscaptured == 1:
            self.ReleaseMouse()#7 釋放鼠標
            self.p2 = event.GetPositionTuple()
            self.shapes.append((self.st, self.p1 + self.p2))
            self.InitBuffer()
            self.iscaptured = 0
        
    def OnMotion(self, event):
        if event.Dragging() and event.LeftIsDown():#8 確定是否在拖動
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)#9 創建另一個緩存的上下文
            self.drawMotion(dc, event)
        event.Skip()
    
    def drawMotion(self, dc, event):
        if self.iscaptured == 1:
            self.p2 = event.GetPositionTuple()
            self.shapes.append((self.st, self.p1 + self.p2))
            self.InitBuffer()
            self.shapes.pop(len(self.shapes)-1)
        
    def OnPaint(self, event):
        wx.BufferedPaintDC(self, self.buffer) # 處理一個paint(描繪)請求
    
    def Draw(self, dc):
        
        if self.img != None:
            dc.DrawBitmap(self.img, 0, 0, False)
        print len(self.shapes)
        for st,(x1,y1,x2,y2) in self.shapes:
            print x1, y1, x2, y2
            if st == 'line':
                dc.DrawLine(x1, y1, x2, y2)
            elif st == 'oval':
                dc.DrawEllipse(x1, y1, x2-x1, y2-y1)
            elif st == 'rect':
                dc.DrawRectangle(x1, y1, x2-x1, y2-y1) 
                
    def getMenu(self):
        menuBar = wx.MenuBar()
        
        menu = wx.Menu()
        m11 = menu.Append(-1, "新建")
        self.Bind(wx.EVT_MENU, self.newFile, m11)
        m12 = menu.Append(-1, "打開")
        self.Bind(wx.EVT_MENU, self.openFile, m12)
        m13 = menu.Append(-1, "保存")
        
        menu.AppendSeparator()
        exit = menu.Append(-1, "退出")
        menuBar.Append(menu, "文件")
        
        menu2 = wx.Menu()
        menu2.Append(-1, "撤銷")
        menu2.Append(-1, "清除")
        menuBar.Append(menu2, "編輯")
        
        menu3 = wx.Menu()
        menuBar.Append(menu3, "圖像")
        
        menu4 = wx.Menu()
        menuBar.Append(menu4, "幫助")
        return menuBar
        
        
    def newFile(self, event):
        pass
    
    def openFile(self, event):
        wildcard = "JPEG 圖片 (*.jpg)|*.jpg|" \
                "PNG 圖片 (*.png)|*.png|" \
                "BMP 圖片 (*.bmp)|*.bmp|" \
                "All files (*.*)|*.*"
        dialog = wx.FileDialog(None, "選擇一個文件", os.getcwd(), "", wildcard, wx.OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            name = dialog.GetPath()
            print dialog.GetPath()
            jpg = wx.Image(name, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
            self.img = jpg
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            dc.DrawBitmap(jpg, 0, 0, False)
        
if __name__ =='__main__':
    app = wx.PySimpleApp()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()
    
畫圖板 version4

 

 3月19日 更新

  這個版本添加了圖片保存功能。這個功能的實現是直接使用PIL的截圖功能ImageGrab,代碼異常簡單。

import ImageGrab

rectangle = (x, y, x+w, y+h)
img = ImageGrab.grab(rectangle)
img.save("1.jpg", 'JPEG')
圖片保存核心代碼

  經過測試發現,雖然這個方法成功截圖,但是圖片質量會下降,變得稍微模糊。如果畫的是純圖形(直線、矩形、圓等),可以直接保存端點坐標到文件,打開的時候讀取端點重新畫圖形就能完美還原。

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM