wxpython Pan implementation issue - python

I'm trying to implement a panning function in a wxpython program. This program consists of a frame containing a ScrolledWindow which holds a Bitmap image.
Clicking and panning on the blank panel produces the expected behaviour. Clicking and panning on the image produces "jumping" of about 10px.
If the mouse_pos member is not updated during dragging, panning is smooth on the image but then panning on the blank panel is erroneous.
System information: wxpython 2.8.12.1, Python 2.7.3 64bit and Windows 7 64bit.
import wx
import inspect
import threading
class MyFrame(wx.Frame):
def __init__(self, parent, mytitle):
wx.Frame.__init__(self, parent, wx.ID_ANY, mytitle, size=(350, 350))
self.scrollwin = wx.ScrolledWindow(self, wx.ID_ANY)
width = 1000
height = 1000
self.scrollwin.SetScrollbars(20, 20, width/20, height/20)
self.scrollwin.SetScrollRate(1, 1)
self.scrollwin.Bind(wx.EVT_MOTION, self.on_mouse_drag)
self.scrollwin.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
self.SetCursor(wx.StockCursor(wx.CURSOR_HAND))
image_file = "test.jpg"
image = wx.Bitmap(image_file)
sb = wx.StaticBitmap(self.scrollwin, wx.ID_ANY, image)
sb.Bind(wx.EVT_MOTION, self.event_defer)
sb.Bind(wx.EVT_LEFT_DOWN, self.event_defer)
self._mouse_pos = (0, 0)
self.sem = threading.Semaphore(0)
#property
def mouse_pos(self):
return self._mouse_pos
#mouse_pos.setter
def mouse_pos(self, value):
print "mouse_pos:"
print "Calling from: ", inspect.stack()[1][3]
print value
self._mouse_pos = value
def on_mouse_drag(self, event):
if event.Dragging() and event.LeftIsDown():
self.sem.release()
print self.sem._Semaphore__value
(mouse_x, mouse_y) = self.mouse_pos
#new_pos = self.scrollwin.CalcUnscrolledPosition(event.GetPosition()).Get()
#new_pos = self.scrollwin.CalcScrolledPosition(event.GetPosition()).Get()
new_pos = event.GetPositionTuple()
(new_x, new_y) = new_pos
scrollpos = self.scrollwin.GetViewStart()
(scroll_x, scroll_y) = scrollpos
(delta_x, delta_y) = ((new_x - mouse_x), (new_y - mouse_y))
(scroll_x, scroll_y) = ((scroll_x - delta_x), (scroll_y - delta_y))
print "Scroll:"
print (scroll_x, scroll_y)
self.scrollwin.Scroll(scroll_x, scroll_y)
#self.mouse_pos = self.scrollwin.CalcUnscrolledPosition(event.GetPosition()).Get()
#self.mouse_pos = self.scrollwin.CalcScrolledPosition(event.GetPosition()).Get()
self.mouse_pos = new_pos # Disabling this gives smooth panning on the image
self.sem.acquire(False)
def on_left_down(self, event):
#self.mouse_pos = self.scrollwin.CalcUnscrolledPosition(event.GetPosition()).Get()
#self.mouse_pos = self.scrollwin.CalcScrolledPosition(event.GetPosition()).Get()
self.mouse_pos = event.GetPositionTuple()
def event_defer(self, event):
event.ResumePropagation(1)
event.Skip()
app = wx.App()
MyFrame(None, "Test wx.ScrolledWindow()").Show()
app.MainLoop()

I found this by accident, but I tried running it and it looks great, no jumping when I'm panning an image around. I'm running python 2.7.3 on Ubuntu 12.04 (64bit), so maybe there's an issue with Windows but it does work great on Linux.

Related

Making "Snipping Tool" from Python using Wx and PyAutoGui

I am trying to make a snipping tool using the Wx and PyAutoGui modules, I am stuck at a certain problem: The saved image file is at wrong position (see image below)
Picture Link
As you can see I am trying to grab a specific region, that is the red rectangle, and save those pixels in that region to the file "my_screenshot.png". However, the position/coordinates seems to be off (you can see the rectangles, which was supposed to be the region of the screenshot)
Here are the codes:
import wx
import pyautogui
class SelectableFrame(wx.Frame):
c1 = None
c2 = None
def __init__(self, parent=None, id=-1, title=""):
wx.Frame.__init__(self, parent, id, title, size=wx.DisplaySize())
self.panel = wx.Panel(self, size=self.GetSize())
self.panel.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.panel.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
self.panel.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
self.panel.Bind(wx.EVT_PAINT, self.OnPaint)
self.SetCursor(wx.Cursor(wx.CURSOR_CROSS))
self.SetTransparent(50)
def OnMouseMove(self, event):
if event.Dragging() and event.LeftIsDown():
self.c2 = event.GetPosition()
self.Refresh()
def OnMouseDown(self, event):
self.c1 = event.GetPosition()
def OnMouseUp(self, event):
self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
region = (self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
pyautogui.screenshot('my_screenshot.png', region=region)
print("MouseUp: " + str(region))
self.Hide()
def OnPaint(self, event):
if self.c1 is None or self.c2 is None: return
dc = wx.PaintDC(self.panel)
dc.SetPen(wx.Pen('red', 1))
dc.SetBrush(wx.Brush(wx.Colour(0, 0, 0), wx.TRANSPARENT))
region = (self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
dc.DrawRectangle(self.c1.x, self.c1.y, self.c2.x - self.c1.x, self.c2.y - self.c1.y)
print("Draw: " + str(region))
def PrintPosition(self, pos):
return str(pos.x) + " " + str(pos.y)
class MyApp(wx.App):
def OnInit(self):
frame = SelectableFrame()
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
From what I've gathered that, it took the width and height, but wrong x and y position, how can I fix that? Thank you
EDIT: There seems to be values difference between these functions
def OnMouseDown(self, event):
self.c1 = event.GetPosition()
print("MouseDown[event]: " + str(self.c1))
print("MouseDown[gui]: "+ str(pyautogui.position()))
Output:
MouseDown[event]: (729, 484)
MouseDown[gui]: Point(x=737, y=515)
The offset is +8 for x, and +31 for y. How did this incontinence happen? My hotfix would be to add those offsets to the pyautogui.screenshot command but I don't think that is the right fix and would not be guaranteed to be the same offset values for other screen sizes..
The wxpython MouseEvent doc tells
The position associated with a mouse event is expressed in the window
coordinates of the window which generated the event, you can use
wx.Window.ClientToScreen to convert it to screen coordinates and
possibly call wx.Window.ScreenToClient next to convert it to window
coordinates of another window.
pyautogui' screenshot() works with screen coordinates.
So, use wx.Window.ClientToScreen with both your c1 and c2.
BTW, you should update c2 also at OnMouseUp and check it not to be 'None' nor equal to c1.

wxPython: Drag&Drop a wx.Button over a panel crashes the application

I'm trying to build an application where the user can drag&drop some button around the panel.
I first have an error about a mouse capture event lost and I finally found that I have to catch this event to prevent the error.
But now, when I run the application, I can drag&drop the button however the application is totally frozen after I release the mouse's left button.
I have to stop it with Ctrl+C from the terminal otherwise my mouse is unusable in any other windows in my desktop environment.
I suspect a problem of mouse capturing event that is not well handled.
I'm working under Ubuntu 16.04 with Python 3.5 installed from package (apt).
I tried with both wxPython 4.0.0 installed from package (apt) and also with the latest wxPython 4.0.4 installed from pip.
In both cases the application is totally frozen after a click or a drag&drop of the button.
import wx
class DragButton(wx.Button):
def __init__(self, parent, id=wx.ID_ANY, label="", pos=(0, 0)):
super().__init__(parent=parent, id=id, label=label, pos=pos)
self._dragging = False
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, lambda evt: None)
def OnLeftDown(self, evt):
print("Left down")
if not self.HasCapture():
self.CaptureMouse()
x, y = self.ClientToScreen(evt.GetPosition())
originx, originy = self.GetPosition()
dx = x - originx
dy = y - originy
self.delta = ((dx, dy))
def OnLeftUp(self, evt):
print("Left UPPPP")
if self.HasCapture():
self.ReleaseMouse()
def OnMouseMove(self, evt):
if evt.Dragging() and evt.LeftIsDown():
x, y = self.ClientToScreen(evt.GetPosition())
fp = (x - self.delta[0], y - self.delta[1])
self.Move(fp)
class GDomFrame(wx.Frame):
def __init__(self, parent, title):
super().__init__(parent, title=title, size=(350, 300))
self._init_ui()
self.Centre()
def _init_ui(self):
panel = wx.Panel(self)
self.button = DragButton(panel, label="Drag me", pos=(10, 10))
if __name__ == '__main__':
print("wxPython version: {}".format(wx.__version__))
app = wx.App()
ex = GDomFrame(None, title='GDom Application')
ex.Show()
app.MainLoop()
With this code I expect to have a button that I can move around the panel several time.
I have tested a similar script. It works fine on Windows, but not on ubuntu 16.04. I solved the problem like this.
def OnLeftDown(self, evt):
print("Left down")
if not self.HasCapture():
self.CaptureMouse()
self.ReleaseMouse() # <------
My script:
import wx
class Mywin(wx.Frame):
def __init__(self, parent, title):
super(Mywin, self).__init__(parent, title = title,size = (400,200))
self.InitUI()
self.Centre()
def InitUI(self):
self.panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
self.btn = wx.Button(self.panel,-1,"click Me",pos=(10, 10))
vbox.Add(self.btn,0,wx.ALIGN_CENTER)
self.btn.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
self.btn.Bind(wx.EVT_MOTION, self.OnMouseMove)
self.btn.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
print ("Init pos:",self.btn.GetPosition())
def OnMouseDown(self, event):
if (not self.btn.HasCapture()):
self.btn.CaptureMouse()
self.btn.ReleaseMouse()
sx,sy = self.panel.ScreenToClient(self.btn.GetPosition())
dx,dy = self.panel.ScreenToClient(wx.GetMousePosition())
self.btn._x,self.btn._y = (sx-dx, sy-dy)
def OnMouseMove(self, event):
if event.Dragging() and event.LeftIsDown():
x, y = wx.GetMousePosition()
self.btn.SetPosition(wx.Point(x+self.btn._x,y+self.btn._y))
print(self.btn.GetPosition())
def OnMouseUp(self, event):
if (self.btn.HasCapture()):
self.btn.ReleaseMouse()
print ("Final pos:",self.btn.GetPosition())
def main():
app = wx.App()
w = Mywin(None, title='Button demo')
w.Show()
app.MainLoop()
if __name__ == '__main__':
main()

floatcanvas toolbar exact fit sample - worked in python 2, no longer working in 3 w/latest wxPython

The main issue is that the toolbar doesn't open anymore. The original goal was to hide/show the toolbar when the user leaves/enters the window/canvas along w/an exact fit app.
The following code worked w/FloatCanvas & Python 2:
#!/usr/bin/env python
import wx
# #import the installed version
# from wx.lib.floatcanvas import NavCanvas, FloatCanvas
# import a local version
import sys
sys.path.append("../")
from floatcanvas import NavCanvas, FloatCanvas
# Set a path to an Image file here:
ImageFile = "./white_tank.jpg"
class DrawFrame(wx.Frame):
"""
A frame used for the FloatCanvas Demo
"""
def __init__(self, *args, **kwargs):
# wx.Frame.__init__(self, *args, **kwargs)
wx.Frame.__init__(self, *args,
style=wx.FRAME_SHAPED
| wx.SIMPLE_BORDER
| wx.FRAME_NO_TASKBAR
)
# self.CreateStatusBar()
# Add the Canvas
self.CanvasNav = NavCanvas.NavCanvas(self,
ProjectionFun=None,
style=wx.TRANSPARENT_WINDOW,
# BackgroundColor=(255, 255, 255, 0),
# BackgroundColor="DARK SLATE BLUE",
)
self.CanvasNav.ToolBar.Hide()
Canvas = self.CanvasNav.Canvas
Canvas.MaxScale = 4
self.Canvas = Canvas
FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove)
self.CanvasNav.ZoomButton.Bind(wx.EVT_BUTTON, self.ZoomToFit)
# create the image:
image = wx.Image(ImageFile)
img = Canvas.AddScaledBitmap(image,
(0, 0),
Height=image.GetHeight(),
Position='tl',
Quality='normal',
)
self.image = image
self.set_size(image)
img.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.OnLeftDown)
img.Bind(FloatCanvas.EVT_FC_MOTION, self.OnMotion)
self.Bind(wx.EVT_MOVE, self.OnMove)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
self.Show()
self.CanvasNav.ToolBar.Show()
Canvas.ZoomToBB(margin_adjust=1.2)
self.move_count = 0
def ZoomToFit(self, e):
self.set_size(self.image)
self.Canvas.ZoomToBB(margin_adjust=1.2)
def set_size(self, image):
"""
adjusts the size of the window to fit the aspect ration of the image
"""
# compute the aspect ratio of the image
w, h = image.GetSize()
ar = float(w) / float(h)
# check the size of this window
w, h = self.GetSize()
print w, h
# adjust the width to get the rigth aspect ratio
w = int(h * ar)
self.SetSize( (w, h) )
print "reset size to:"
print self.GetSize()
def OnEnter(self, e):
self.CanvasNav.ToolBar.Show()
self.CanvasNav.ToolBar.Realize()
def OnLeave(self, e):
self.CanvasNav.ToolBar.Hide()
self.CanvasNav.ToolBar.Realize()
def OnMove(self, event):
"""
Updates the status bar with the world coordinates
"""
# self.SetStatusText("%i, %i" % tuple(event.Coords))
def OnLeftDown(self, obj):
print "Left Mouse Clicked on ", obj
def OnMotion(self, obj):
print "mouse moving on image:", self.move_count
self.move_count += 1
app = wx.App(False)
F = DrawFrame(None, title="FloatCanvas Demo App", size=(700, 700))
app.MainLoop()
Here's my attempt to get it to work the same way using the latest Python & wxPython, but am having issues w/the toolbar (also getting KeyError on line 64).
#!/usr/bin/env python
import wx
# #import the installed version
# from wx.lib.floatcanvas import NavCanvas, FloatCanvas
# import a local version
import sys
sys.path.append("../")
try:
from floatcanvas import NavCanvas, FloatCanvas, Resources
except ImportError: # if it's not there locally, try the wxPython lib.
from wx.lib.floatcanvas import NavCanvas, FloatCanvas, Resources
# Set a path to an Image file here:
# ImageFile = "C:\\Users\\alex\\Downloads\\white_tank.jpg"
ImageFile = "white_tank.jpg"
class DrawFrame(wx.Frame):
"""
A frame used for the FloatCanvas Demo
"""
def __init__(self, *args, **kwargs):
# wx.Frame.__init__(self, *args, **kwargs)
wx.Frame.__init__(self, *args,
style=wx.FRAME_SHAPED
| wx.SIMPLE_BORDER
| wx.FRAME_NO_TASKBAR
)
# self.CreateStatusBar()
# Add the Canvas
self.CanvasNav = NavCanvas.NavCanvas(self,
ProjectionFun=None,
style=wx.TRANSPARENT_WINDOW,
# BackgroundColor=(255, 255, 255, 0),
# BackgroundColor="DARK SLATE BLUE",
)
self.CanvasNav.ToolBar.Hide()
Canvas = self.CanvasNav.Canvas
Canvas.MaxScale = 4
self.Canvas = Canvas
FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove)
self.CanvasNav.ZoomButton.Bind(wx.EVT_BUTTON, self.ZoomToFit)
# create the image:
image = wx.Image(ImageFile)
img = Canvas.AddScaledBitmap(image,
(0, 0),
Height=image.GetHeight(),
Position='tl',
# Quality='normal',
)
self.image = image
self.set_size(image)
img.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.OnLeftDown)
# img.Bind(FloatCanvas.EVT_FC_MOTION, self.OnMotion)
self.Bind(wx.EVT_MOVE, self.OnMove)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
self.Show()
self.CanvasNav.ToolBar.Show()
Canvas.ZoomToBB(margin_adjust=1.2)
self.move_count = 0
def ZoomToFit(self, e):
self.set_size(self.image)
self.Canvas.ZoomToBB(margin_adjust=1.2)
def set_size(self, image):
"""
adjusts the size of the window to fit the aspect ration of the image
"""
# compute the aspect ratio of the image
w, h = image.GetSize()
ar = float(w) / float(h)
# check the size of this window
w, h = self.GetSize()
print(w, h)
# adjust the width to get the rigth aspect ratio
w = int(h * ar)
self.SetSize( (w, h) )
print("reset size to:")
print(self.GetSize())
def OnEnter(self, e):
self.CanvasNav.ToolBar.Show()
self.CanvasNav.ToolBar.Realize()
def OnLeave(self, e):
self.CanvasNav.ToolBar.Hide()
self.CanvasNav.ToolBar.Realize()
def OnMove(self, event):
"""
Updates the status bar with the world coordinates
"""
# self.SetStatusText("%i, %i" % tuple(event.Coords))
def OnLeftDown(self, obj):
print("Left Mouse Clicked on ", obj)
def OnMotion(self, obj):
print("mouse moving on image:", self.move_count)
self.move_count += 1
app = wx.App(False)
F = DrawFrame(None, title="FloatCanvas Demo App", size=(700, 700))
app.MainLoop()
It is working using python 3.6.7 and wxpython 4.0.3 gtk2 but fails when using wxpython 4.0.1 gtk3. [On Linux]
You failed to specify your OS and the version of wx that you are using.
If we assume that you are using 4.0.1 gtk3 the answer is to change the style of the DrawFrame from
wx.Frame.__init__(self, *args,style=wx.FRAME_SHAPED| wx.SIMPLE_BORDER)| wx.FRAME_NO_TASKBAR)
to
wx.Frame.__init__(self, *args)
Note also that margin_adjust in Canvas.ZoomToBB(margin_adjust=1.2) is now invalid.
I would also worry about the fact that you are trying to import a local version of floatcanvas
sys.path.append("../")
try:
from floatcanvas import NavCanvas, FloatCanvas, Resources
except ImportError: # if it's not there locally, try the wxPython lib.
from wx.lib.floatcanvas import NavCanvas, FloatCanvas, Resources

How to move window out of display with wxpython on linux

i have a little GUI app, which runs on Windows 7 and 10 well. With self.Move(x,y) I can move the window, if x, or y is negative, part of my window or the whole window is out of screen. I want to achieve this behavior on Linux. On ubuntu 16.04 I tried on unity and kde, but it didnt work, the whole window is always visible. Can you show me way to move my window out of screen on linux?
import wx
class MainFrame(wx.Frame):
def __init__(self, *args, **kwds):
# kwds["pos"] = (10,10)
self.frame = wx.Frame.__init__(self, *args, **kwds)
self.SetTitle("Move around the screen")
self.InitUI()
def InitUI(self):
self.location1 = wx.Point(-30,-100)
self.location2 = wx.Point(500,500)
self.panel1 = wx.Panel(self)
self.button1 = wx.Button(self.panel1, -1, label="Move", size=(80,25), pos=(10,10))
self.button1.Bind(wx.EVT_BUTTON, self.OnItem1Selected)
self.Show()
self.Move(self.location1)
def OnItem1Selected(self, event):
self.MoveAround()
def MoveAround(self):
curr_location = self.GetPosition() #or self.GetPositionTuple()
if curr_location == self.location1:
print ("moving to ", self.location2)
self.Move(self.location2)
else:
print ("moving to ", self.location1)
self.Move(self.location1)
if __name__ == '__main__':
app = wx.App()
frame = MainFrame(None)
app.MainLoop()

Flicker-free drawable ScrolledWindow

I'm trying to build a ScrolledWindow that you can draw on using the mouse, and it's working too, but I'm getting a nasty flicker when the user is drawing on the window while the scrollbars aren't in the "home" position..
To reproduce, run the attached program, scroll a bit down (or to the right) and "doodle" a bit by keeping the left mouse button pressed. You should see a flickering now and then..
import wx
class MainFrame(wx.Frame):
""" Just a frame with a DrawPane """
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
s = wx.BoxSizer(wx.VERTICAL)
s.Add(DrawPane(self), 1, wx.EXPAND)
self.SetSizer(s)
########################################################################
class DrawPane(wx.PyScrolledWindow):
""" A PyScrolledWindow with a 1000x1000 drawable area """
VSIZE = (1000, 1000)
def __init__(self, *args, **kw):
wx.PyScrolledWindow.__init__(self, *args, **kw)
self.SetScrollbars(10, 10, 100, 100)
self.prepare_buffer()
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
self.Bind(wx.EVT_MOTION, self.on_motion)
def prepare_buffer(self):
self.buffer = wx.EmptyBitmap(*DrawPane.VSIZE)
dc = wx.BufferedDC(None, self.buffer)
dc.Clear()
dc.DrawLine(0, 0, 999, 999) # Draw something to better show the flicker problem
def on_paint(self, evt):
dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
def on_mouse_down(self, evt):
self.mouse_pos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
def on_motion(self, evt):
if evt.Dragging() and evt.LeftIsDown():
dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
newpos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
coords = self.mouse_pos + newpos
dc.DrawLine(*coords)
self.mouse_pos = newpos
self.Refresh()
if __name__ == "__main__":
app = wx.PySimpleApp()
wx.InitAllImageHandlers()
MainFrame(None).Show()
app.MainLoop()
I tried using SetBackgroundStyle(wx.BG_STYLE_CUSTOM), or binding EVT_ERASE_BACKGROUND, or using RefreshRect instead of Refresh, but the flicker is still there.. Any idea on what I might try next?
My environment: Xubuntu 9.04, wxPython 2.8.9.1
(but tested on Ubuntu 10.04 too)
Many thanks for your time!
From Robin Dunn himself:
First, a Refresh() by default will
erase the background before sending
the paint event (although setting the
BG style or catching the erase event
would have taken care of that.) The
second and probably most visible
problem in this case is that in your
on_motion handler you are not
offsetting the ClientDC by the scroll
offsets, just the position in the
buffer that you are drawing the line
segment at. So when the buffer is
flushed out to the client DC it is
drawn at the physical (0,0), not the
virtual (0,0). In other words, the
flicker you are seeing is coming from
drawing the buffer at the wrong
position after every mouse drag event,
and then it immediately being drawn
again at the right position in the
on_paint triggered by the
Refresh().
You should be able to fix this by
calling PrepareDC on the client DC
before using it, like this:
cdc = wx.CLientDC(self)
self.PrepareDC(cdc)
dc = wx.BufferedDC(cdc, self.buffer)
However since you are doing a
Refresh or RefreshRect anyway,
there is no need to use a client DC
here at all, just let the flushing of
the buffer to the screen be done in
on_paint instead:
dc = wx.BufferedDC(None, self.buffer)
Using Joril recomendations and removing Refresh(), there is no flicker anymore (even enlarging the frame).
import wx
class MainFrame(wx.Frame):
""" Just a frame with a DrawPane """
def __init__(self, *args, **kw):
wx.Frame.__init__(self, *args, **kw)
s = wx.BoxSizer(wx.VERTICAL)
s.Add(DrawPane(self), 1, wx.EXPAND)
self.SetSizer(s)
########################################################################
class DrawPane(wx.PyScrolledWindow):
""" A PyScrolledWindow with a 1000x1000 drawable area """
VSIZE = (1000, 1000)
def __init__(self, *args, **kw):
wx.PyScrolledWindow.__init__(self, *args, **kw)
self.SetScrollbars(10, 10, 100, 100)
self.prepare_buffer()
cdc = wx.ClientDC(self)
self.PrepareDC(cdc)
dc = wx.BufferedDC(cdc, self.buffer)
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
self.Bind(wx.EVT_MOTION, self.on_motion)
def prepare_buffer(self):
self.buffer = wx.EmptyBitmap(*DrawPane.VSIZE)
cdc = wx.ClientDC(self)
self.PrepareDC(cdc)
dc = wx.BufferedDC(cdc, self.buffer)
dc.Clear()
dc.DrawLine(0, 0, 999, 999) # Draw something to better show the flicker problem
def on_paint(self, evt):
dc = wx.BufferedPaintDC(self, self.buffer, wx.BUFFER_VIRTUAL_AREA)
def on_mouse_down(self, evt):
self.mouse_pos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
def on_motion(self, evt):
if evt.Dragging() and evt.LeftIsDown():
newpos = self.CalcUnscrolledPosition(evt.GetPosition()).Get()
coords = self.mouse_pos + newpos
cdc = wx.ClientDC(self)
self.PrepareDC(cdc)
dc = wx.BufferedDC(cdc, self.buffer)
dc.DrawLine(*coords)
self.mouse_pos = newpos
if __name__ == "__main__":
app = wx.PySimpleApp()
wx.InitAllImageHandlers()
MainFrame(None).Show()
app.MainLoop()

Categories