2012-03-29 10 views
0

我正在使用wxPyhon編寫簡單的GUI並面臨一些問題。
我的應用程序做了簡單的事情:它在窗體上繪製三角形,並在用戶單擊箭頭按鈕或在窗體上拖動鼠標光標時旋轉它。
我現在看到的問題如下:
1.然後,我快速拖動鼠標sursor,三角形旋轉,同時保持舊圖像短時間可見。當保持快速移動光標一段時間時,窗體上的圖形看起來像2或3個三角形。
2.如果我將窗體展開爲整個屏幕的大小,三角形將不太順暢地移動,從舊的外觀跳到一個新的外觀。我在旋轉過程中查看了鼠標光標的座標,並注意到它們被間隙跟蹤。我的朋友說我是因爲每當我揮動三角形時,我都會重新繪製應用程序的整個窗口。這就是爲什麼它運行緩慢並且減慢了鼠標光標的跟蹤速度。如何平滑重繪窗體上的對象(wxPython)

刷新視圖我正在使用wx.Panel.Refresh()方法。作爲繪圖上下文,我正在使用wx.BufferedDC()

請告訴我如何在wxPython表單上正確繪製動態更改的圖片/繪圖,特別是我在該應用程序中製作的方式。

我可以把我的代碼放在這裏,但它太長了。所以如果我必須告訴我更多關於我的情況 - 請問我,我會回答。

謝謝!

class SimpleGraphics(wx.Panel): 
    def __init__(self, parent, size=(50, 50)): 
     super(SimpleGraphics, self).__init__(parent, 
            size=size, 
            style=wx.NO_BORDER) 

     self.color = "Black" 
     self.thickness = 2 
     self.pen = wx.Pen(self.color, self.thickness, wx.SOLID) 
     self.MARGIN = 1 #px 
     self.points = [[0.0, 0.5], [0.5, 0.0], [-0.5, -0.5]] 
     self.pos = (0, 0) 
     self.cur_vector = Vector2D(1, 1) 

     self.InitBuffer() 
     self.Bind(wx.EVT_SIZE, self.OnSize) 
     self.Bind(wx.EVT_IDLE, self.OnIdle) 
     self.Bind(wx.EVT_KEY_DOWN, self.OnKeyArrow) 
     # MOUSE TRACKING 
     self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) 
     self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) 
     self.Bind(wx.EVT_MOTION, self.OnMotion) 

     self.Bind(wx.EVT_PAINT, self.OnPaint) 


    def InitBuffer(self): 
     self.client_size = self.GetClientSize() 
     self.buffer = wx.EmptyBitmap(self.client_size.width, self.client_size.height) 
     dc = wx.BufferedDC(None, self.buffer) 
     dc.SetBackground(wx.Brush(self.GetBackgroundColour())) 
     dc.Clear() 
     self.DrawImage(dc) 
     self.reInitBuffer = False 

    def OnSize(self, event): 
     self.reInitBuffer = True 

    def repaint_the_view(self): 
     self.InitBuffer() 
     self.Refresh() 

    def OnIdle(self, event): 
     if self.reInitBuffer: 
      self.repaint_the_view() 

    def OnKeyArrow(self, event): 
     key_code = event.GetKeyCode() 
     if key_code == wx.WXK_LEFT: 
      self.rotate_points(degrees_to_rad(5)) 
     elif key_code == wx.WXK_RIGHT: 
      self.rotate_points(degrees_to_rad(-5)) 
     self.repaint_the_view() 
     event.Skip() 

    def OnLeftDown(self, event): 
     # get the mouse position and capture the mouse 
     self.pos = event.GetPositionTuple() 
     self.cur_vector = create_vector2d(self.pos[0], self.pos[1], 
             self.client_size.width/2, 
             self.client_size.height/2) 
     self.CaptureMouse() 

    def OnLeftUp(self, event): 
     #release the mouse 
     if self.HasCapture(): 
      self.ReleaseMouse() 

    def OnMotion(self, event): 
     if event.Dragging() and event.LeftIsDown(): 
      newPos = event.GetPositionTuple() 
      new_vector = create_vector2d(newPos[0], newPos[1], 
             self.client_size.width/2, 
             self.client_size.height/2) 
      if new_vector.lenth() > 0.00001: 
       c = cos_a(self.cur_vector, new_vector) 
       s = sin_a(self.cur_vector, new_vector) 
       rot_matr = rotation_matrix(s, c) 
       self.rotate_points(rot_matr=rot_matr) 
       dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) # this line I've added after posting the question 
       self.repaint_the_view() 
       self.cur_vector = new_vector 
     event.Skip() 

    def OnPaint(self, event): 
     wx.BufferedPaintDC(self, self.buffer) 

    def DrawImage(self, dc): 
     dc.SetPen(self.pen) 
     new_points = self.convetr_points_to_virtual() 
     dc.DrawPolygon([wx.Point(x, y) for (x, y) in new_points]) 

    def to_x(self, X_Log): 
     X_Window = self.MARGIN + (1.0/2) * (X_Log + 1) * (self.client_size.width - 2 * self.MARGIN) 
     return int(X_Window) 

    def to_y(self, Y_Log): 
     Y_Window = self.MARGIN + (-1.0/2) * (Y_Log - 1) * (self.client_size.height - 2 * self.MARGIN) 
     return int(Y_Window) 

    def convetr_points_to_virtual(self): 
     return [(self.to_x(x), self.to_y(y)) for (x, y) in self.points] 

    def rotate_points(self, angle_in_degrees=None, rot_matr=None): 
     if angle_in_degrees is None: 
      self.points = [rotate_point(x, y , rotator_matrix=rot_matr) for (x, y) in self.points] 
     else: 
      self.points = [rotate_point(x, y , angle_in_degrees) for (x, y) in self.points] 

class SimpleGraphicsFrame(wx.Frame): 
    def __init__(self, parent, *args, **kwargs): 
     wx.Frame.__init__(self, parent, *args, **kwargs) 

     # Attributes 
     self.panel = SimpleGraphics(self) 

     # Layout 
     sizer = wx.BoxSizer(wx.VERTICAL) 
     sizer.Add(self.panel, 1, wx.EXPAND) 
     self.SetSizer(sizer) 

class SimpleGraphApp(wx.App): 
    def OnInit(self): 
     self.frame = SimpleGraphicsFrame(None, 
           title="Drawing Shapes", 
           size=(300, 400)) 
     self.frame.Show() 
     return True 

if __name__ == '__main__': 
    app = SimpleGraphApp(False) 
    app.MainLoop() 
+1

嘗試在EVT_IDLE中刷新面板,而不是在鼠標事件中。也使用雙緩衝。如果您可以提供演示問題的小型可運行代碼示例,將會有所幫助。 – Fenikso 2012-03-30 14:04:06

+0

我已添加我的代碼。一些功能不包括在內,但我認爲,給定的部分足以實現這個想法。 P.S. 我在OnMotion方法中添加了一行。它使應用程序更好(解決項目2),但我認爲這個應用程序不夠好。所以你的建議將受到歡迎。 – 2012-03-30 17:46:28

回答

0

你打電話從OnKeyArrowOnMotion事件self.Refresh()。用這些方法更新你的場景數據並設置一些標誌,例如self.repaint_needed = True。然後在OnIdle重繪場景如果self.repaint_needed爲真。

現在您每次收到事件時都嘗試重新繪製窗口。其中可能有很多

你想要做的是每次更新場景信息,但只有當wx指示它有一些「空閒時間」時重新繪製窗口。

+0

我照你的建議做了,但是用'self.reInitBuffer'作爲標誌。說實話,我沒有注意到任何變化。我的意思是將'dc = wx.BufferedDC(wx.ClientDC(self),self.buffer)'字符串添加到'def OnMotion()'方法中,這是主要的收益。無論如何感謝您的建議,我認爲它真的更好地刷新空閒時間的油漆 – 2012-04-06 08:29:52

+0

嗯,仍然可能涉及更多的東西,你仍然可以重繪它在一些地方,這是沒有必要的。我將不得不通過您的更新代碼並再次檢查。 – Fenikso 2012-04-06 13:44:36